You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2020/09/20 11:47:40 UTC

[isis] 01/02: ISIS-2222 : reworking command (log and replay); other small improvements.

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

danhaywood pushed a commit to branch ISIS-2222
in repository https://gitbox.apache.org/repos/asf/isis.git

commit 228435a384f2f6b9d3b3b386befef2a3c9f9a32a
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Sun Sep 20 11:51:52 2020 +0100

    ISIS-2222 : reworking command (log and replay); other small improvements.
---
 antora/playbooks/site-extensions.yml               |   6 +
 antora/playbooks/site.yml                          |   9 +
 api/adoc/pom.xml                                   |  40 ++
 .../modules/ROOT/partials/component-nav.adoc       |   1 +
 .../applib-ant/examples/annotation/Action.java     |  32 +-
 .../examples/annotation/CommandExecuteIn.java      |  83 ---
 .../examples/annotation/CommandPersistence.java    |  52 --
 .../applib-ant/examples/annotation/Nature.java     |  16 +
 .../applib-ant/examples/annotation/Property.java   |  44 +-
 .../main/adoc/modules/applib-ant/pages/Action.adoc |   2 +-
 .../modules/applib-ant/pages/Action/command.adoc   |  32 +-
 .../adoc/modules/applib-ant/pages/Property.adoc    |   1 +
 .../modules/applib-ant/pages/Property/command.adoc | 194 +-----
 .../examples/domain/DomainObjectList.java          |   2 +-
 .../events/domain/AbstractDomainEvent.java         |   4 +-
 .../examples/events/domain/ActionDomainEvent.java  |  16 +-
 .../events/domain/PropertyDomainEvent.java         |  24 +-
 .../layout/component/PropertyLayoutData.java       |  12 -
 .../layout/menubars/{Menu.java => HasNamed.java}   |   4 +-
 .../examples/layout/menubars/Menu.java             |   7 +-
 .../examples/layout/menubars/MenuSection.java      |   2 +-
 .../layout/menubars/bootstrap3/BS3MenuSection.java |  12 +
 .../mixins/layout/Object_rebuildMetamodel.java     |  22 +-
 .../applib-classes/examples/util/ToString.java     |  17 +-
 .../examples/util/schema/CommandDtoUtils.java      |  17 +-
 .../examples/util/schema/CommonDtoUtils.java       |  51 +-
 .../applib-classes/examples/value/Blob.java        |  48 ++
 .../applib-classes/examples/value/Clob.java        |  50 ++
 .../applib-classes/examples/value/HasHtml.java     |   5 -
 .../examples/value/LocalResourcePath.java          |  24 +-
 .../applib-classes/examples/value/Markup.java      |  40 +-
 .../applib-classes/examples/value/Password.java    |  19 +-
 .../roles-mixins-contributees/contributee.adoc     |  12 +-
 .../examples/services/DomainChangeRecord.java      | 155 +++++
 .../DomainChangeRecord_openTargetObject.java       |  61 ++
 .../applib-svc/examples/services/HasUsername.java  |   4 +
 .../services/TransactionScopeListener.java         |   9 +-
 .../background/BackgroundCommandService.java       |  59 --
 .../examples/services/bookmark/Bookmark.java       |   1 +
 .../examples/services/clock/ClockService.java      |  11 +-
 .../examples/services/command/Command.java         | 458 ++++++--------
 .../examples/services/command/CommandDefault.java  | 182 ------
 .../services/command/CommandExecutorService.java   |  19 +-
 .../services/command/CommandOutcomeHandler.java    |  24 +
 .../{CommandContext.java => CommandService.java}   |  62 +-
 .../examples/services/command/package-info.java    |   7 -
 .../services/command/spi/CommandService.java       |  65 --
 .../CommandServiceListener.java}                   |  27 +-
 .../services/commanddto/HasCommandDto.java         |  12 +
 .../conmap/ContentMappingServiceForCommandDto.java | 106 ++++
 .../ContentMappingServiceForCommandsDto.java       |   2 +-
 .../services/commanddto/conmap/UserDataKeys.java}  |  19 +-
 .../processor}/CommandDtoProcessor.java            |  34 +-
 .../CommandDtoProcessorForActionAbstract.java      |   6 +-
 .../CommandDtoProcessorForPropertyAbstract.java    |   6 +-
 .../processor}/spi/CommandDtoProcessorService.java |  26 +-
 .../spi/CommandDtoProcessorServiceIdentity.java    |  23 +
 .../ContentMappingServiceForCommandDto.java        | 156 -----
 .../services/exceprecog/ExceptionRecognizer.java   |  31 +-
 .../examples/services/iactn/Interaction.java       | 153 +++--
 .../services/iactn/InteractionContext.java         |  32 +-
 .../examples/services/jaxb/JaxbService.java        |  20 +-
 .../examples/services/jaxb/JaxbServiceDefault.java |   4 +-
 .../services/metamodel/MetaModelService.java       |   6 +-
 .../queryresultscache/QueryResultsCache.java       |   4 +-
 .../examples/services/scratchpad/Scratchpad.java   |  10 +-
 .../examples/services/sudo/SudoService.java        |   9 +-
 .../TableColumnOrderForCollectionTypeAbstract.java |  65 ++
 .../services/wrapper/control/AsyncControl.java     |  18 +-
 .../wrapper/control/AsyncControlService.java       |  52 --
 .../services/wrapper/control/ControlAbstract.java  |  27 +-
 .../services/xactn/TransactionService.java         |  15 -
 .../modules/applib-svc/pages/BookmarkService.adoc  |   1 -
 .../modules/applib-svc/pages/CommandContext.adoc   |  40 +-
 .../modules/applib-svc/pages/CommandService.adoc   |   9 +-
 .../applib-svc/pages/InteractionContext.adoc       |  10 +-
 .../pages/_BackgroundCommandService.adoc           | 107 ----
 .../applib-svc/pages/_BackgroundService.adoc       | 120 ----
 .../pages/_BackgroundService/_Quartz.adoc          | 165 ------
 .../modules/applib-svc/partials/module-nav.adoc    |   4 -
 .../org/apache/isis/applib/IsisModuleApplib.java   |  16 +-
 .../org/apache/isis/applib/annotation/Action.java  |  32 +-
 .../isis/applib/annotation/CommandExecuteIn.java   |  83 ---
 .../isis/applib/annotation/CommandPersistence.java |  52 --
 .../apache/isis/applib/annotation/Property.java    |  44 +-
 .../applib/events/domain/AbstractDomainEvent.java  |   4 +-
 .../mixins/layout/Object_rebuildMetamodel.java     |  22 +-
 .../isis/applib/services/DomainChangeRecord.java   | 155 +++++
 .../DomainChangeRecord_openTargetObject.java       |  61 ++
 .../apache/isis/applib/services/HasUsername.java   |   4 +
 .../applib/services/TransactionScopeListener.java  |   9 +-
 .../background/BackgroundCommandService.java       |  59 --
 .../isis/applib/services/bookmark/Bookmark.java    |   1 +
 .../isis/applib/services/clock/ClockService.java   |  11 +-
 .../isis/applib/services/command/Command.java      | 458 ++++++--------
 .../applib/services/command/CommandDefault.java    | 182 ------
 .../services/command/CommandExecutorService.java   |  19 +-
 .../services/command/CommandOutcomeHandler.java    |  24 +
 .../{CommandContext.java => CommandService.java}   |  68 +--
 .../applib/services/command/CommandWithDto.java    |  35 --
 .../isis/applib/services/command/package-info.java |   7 -
 .../services/command/spi/CommandService.java       |  65 --
 .../command/spi/CommandServiceListener.java        |  58 +-
 .../applib/services/commanddto/HasCommandDto.java  |  12 +
 .../conmap/ContentMappingServiceForCommandDto.java | 106 ++++
 .../ContentMappingServiceForCommandsDto.java       |   2 +-
 .../services/commanddto/conmap/UserDataKeys.java}  |  19 +-
 .../processor}/CommandDtoProcessor.java            |  34 +-
 .../CommandDtoProcessorForActionAbstract.java      |   6 +-
 .../CommandDtoProcessorForPropertyAbstract.java    |   6 +-
 .../processor}/spi/CommandDtoProcessorService.java |  26 +-
 .../spi/CommandDtoProcessorServiceIdentity.java    |  23 +
 .../ContentMappingServiceForCommandDto.java        | 157 -----
 .../isis/applib/services/iactn/Interaction.java    | 152 +++--
 .../applib/services/iactn/InteractionContext.java  |  33 +-
 .../services/metamodel/MetaModelService.java       |   6 +-
 .../queryresultscache/QueryResultsCache.java       |   4 +-
 .../applib/services/scratchpad/Scratchpad.java     |  10 +-
 .../isis/applib/services/sudo/SudoService.java     |   9 +-
 .../TableColumnOrderForCollectionTypeAbstract.java |  65 ++
 .../services/wrapper/control/AsyncControl.java     |  18 +-
 .../wrapper/control/AsyncControlService.java       |  52 --
 .../services/wrapper/control/ControlAbstract.java  |  10 +-
 .../applib/services/xactn/TransactionService.java  |  15 -
 .../java/org/apache/isis/applib/util/ToString.java |  17 +-
 .../isis/applib/util/schema/CommandDtoUtils.java   |  25 +-
 .../interaction/InteractionDefaultTest_next.java   |   2 +-
 .../wrapper/control/AsyncControl_Test.java         |  13 +-
 .../applib/util/schema/CommandDtoUtils_Test.java   |  13 +-
 .../commons/internal/exceptions/_Exceptions.java   |  14 +
 .../adoc/modules/config/pages/sections/Other.adoc  |   2 +-
 .../adoc/modules/config/pages/sections/_nav.adoc   |   2 +-
 .../modules/config/pages/sections/isis.applib.adoc |  54 +-
 .../pages/sections/isis.core.meta-model.adoc       |   2 +-
 .../isis.core.meta-model.introspector.adoc         |   8 +-
 .../sections/isis.core.meta-model.validator.adoc   |   6 +-
 .../pages/sections/isis.core.runtime-services.adoc |  24 +-
 .../config/pages/sections/isis.core.runtime.adoc   |   4 +-
 .../config/pages/sections/isis.extensions.adoc     | 110 +++-
 .../modules/config/pages/sections/isis.legacy.adoc |   4 +-
 .../isis.persistence.jdo-datanucleus.impl.adoc     |  56 +-
 .../config/pages/sections/isis.security.shiro.adoc |   2 +-
 .../config/pages/sections/isis.testing.adoc        |   2 +-
 .../config/pages/sections/isis.value-types.adoc    |  18 +-
 .../pages/sections/isis.viewer.restfulobjects.adoc |  20 +-
 .../config/pages/sections/isis.viewer.wicket.adoc  |  54 +-
 .../modules/config/pages/sections/resteasy.adoc    |  29 +-
 .../apache/isis/core/config/IsisConfiguration.java | 131 +++-
 .../config/environment/IsisSystemEnvironment.java  |  34 +-
 .../presets/DebugRequestScopedServices.properties  |   1 -
 .../command/CommandFacetForActionAnnotation.java   |  16 +-
 ...ommandFacetForActionAnnotationAsConfigured.java |   8 +-
 .../command/CommandFacetFromConfiguration.java     |   9 +-
 ...ctionInvocationFacetForDomainEventAbstract.java | 130 ++--
 .../actions/action/invocation/CommandUtil.java     |  26 +
 .../facets/actions/command/CommandFacet.java       |  20 +-
 .../actions/command/CommandFacetAbstract.java      |  40 +-
 .../property/PropertyAnnotationFacetFactory.java   |   2 +-
 .../command/CommandFacetForPropertyAnnotation.java |  57 +-
 ...mandFacetForPropertyAnnotationAsConfigured.java |   7 +-
 ...tySetterOrClearFacetForDomainEventAbstract.java | 248 ++++----
 .../facets/schema/IsisSchemaValueTypeProvider.java |   2 +-
 .../services/appfeat/ApplicationFeatureId.java     |   4 +-
 .../command/CommandDtoServiceInternal.java         |  17 +-
 .../services/metamodel/MetaModelExporter.java      |   2 +-
 .../metamodel/MetaModelServiceDefault.java         |   2 +-
 .../specloader/specimpl/ObjectActionDefault.java   |  57 +-
 .../specloader/specimpl/ObjectMemberAbstract.java  |  89 +--
 .../specimpl/OneToOneAssociationDefault.java       |  36 +-
 .../metamodel/valuetypes/ValueTypeDefinition.java  |   6 +-
 .../valuetypes/ValueTypeProviderForBuiltin.java    |   7 +-
 .../action/ActionAnnotationFacetFactoryTest.java   |   6 -
 .../ActionAnnotationFacetFactoryTest_Command.java  | 205 +------
 .../DomainObjectLayoutFactoryTest.java             |   5 +-
 .../ImageValueSemanticsProviderAbstractTest.java   |   4 -
 core/pom.xml                                       |   8 +-
 .../isis/core/runtime/iactn/IsisInteraction.java   |  12 +-
 .../core/runtime/iactn/IsisInteractionFactory.java |  38 +-
 .../template/AbstractIsisInteractionTemplate.java  |  76 ---
 .../transaction/ChangedObjectsService.java         |   1 -
 .../transaction/IsisTransactionAspectSupport.java  |  13 +-
 .../core/runtime/session/IsisRequestCycle.java     |   2 +-
 .../IsisModuleCoreRuntimeServices.java             |   3 -
 .../background/CommandExecutionAbstract.java       |  58 --
 .../command/CommandDtoServiceInternalDefault.java  |  54 +-
 .../command/CommandExecutorServiceDefault.java     | 193 +++---
 .../publish/PublishedObjectsDefault.java           |  20 +-
 .../publish/PublisherDispatchServiceDefault.java   |  46 +-
 .../QueryResultsCacheDefault.java                  |  44 +-
 .../scratchpad/ScratchpadDefault.java              |   4 +-
 .../session/IsisInteractionFactoryDefault.java     |  66 ++-
 .../runtimeservices/sudo/SudoServiceDefault.java   |  14 +-
 .../wrapper/WrapperFactoryDefault.java             | 112 ++--
 .../xactn/TransactionServiceSpring.java            |  14 +
 .../WrapperFactoryDefault_wrappedObject_Test.java  | 506 ----------------
 ...actoryDefault_wrappedObject_transient_Test.java | 304 ----------
 examples/demo/domain/pom.xml                       |  17 +-
 .../src/main/java/demoapp/dom/DemoModule.java      |   8 +-
 .../ActionCommandDisabledMetaAnnotation.java       |   5 +-
 .../Action/command/ActionCommandJdo.java           |  72 +--
 .../Action/command/ActionCommandJdo.layout.xml     |  29 +-
 .../ActionCommandJdo_mixinUpdateProperty.java      |   5 +-
 ...andJdo_mixinUpdatePropertyCommandDisabled.java} |  12 +-
 ...mmandJdo_mixinUpdatePropertyMetaAnnotation.java |  10 +-
 ...ixinUpdatePropertyMetaAnnotationOverridden.java |   9 +-
 .../ActionCommandJdo_backgroundCommands.java       |  39 --
 .../ActionCommandJdo_clearBackgroundCommands.java  |  38 --
 .../ActionCommandJdo_clearForegroundCommands.java  |  38 --
 ...ActionCommandJdo_executeBackgroundCommands.java |  39 --
 .../ActionCommandJdo_foregroundCommands.java       |  39 --
 .../spiimpl/CommandServiceSpiForActions.java       |  95 ---
 .../Action/publishing/ActionPublishingJdo.java     |   4 +-
 .../publishing/ActionPublishingJdo.layout.xml      |   2 +-
 ...onPublishingJdo_clearInteractionExecutions.java |  34 --
 .../ActionPublishingJdo_interactionExecutions.java |  39 --
 .../publishing/DomainObjectPublishingJdo.java      |  19 +-
 .../DomainObjectPublishingVm-description.adoc      |   2 +-
 .../DomainObjectPublishingVm_delete.java           |  30 +-
 .../DomainObjectPublishingDisabledJdoEntities.java |   6 +-
 .../DomainObjectPublishingEnabledJdoEntities.java  |   4 +-
 ...tPublishingEnabledMetaAnnotatedJdoEntities.java |   6 +-
 ...shingEnabledMetaAnnotOverriddenJdoEntities.java |   6 +-
 .../DomainObjectPublishingVm_publishedObjects.java |  39 --
 .../Property/publishing/PropertyPublishingJdo.java |   4 +-
 ...tyPublishingJdo_clearInteractionExecutions.java |  34 --
 ...ropertyPublishingJdo_interactionExecutions.java |  40 --
 .../spiimpl/PublisherServiceSpiForProperties.java  |  48 --
 ...tyRegexPatternVm_updateWithParameterLayout.java |   2 +-
 .../RegexPatternEmailComMetaAnnotation.java        |  14 +-
 .../_changes/ExposeCapturedChanges.java            |   9 +
 .../_changes/ExposeCapturedChanges_changes.java    |  35 ++
 .../ExposeCapturedChanges_clear.java}              |  12 +-
 .../PublisherServiceToCaptureChangesInMemory.java} |   4 +-
 .../_commands/ExposePersistedCommands.java         |  54 ++
 .../ExposePersistedCommands_commands.java          |  33 ++
 .../_interactions/ExposeCapturedInteractions.java  |   9 +
 .../ExposeCapturedInteractions_clear.java          |  31 +
 .../ExposeCapturedInteractions_interactions.java   |  35 ++
 .../_interactions/InteractionDtoVm.java            |  62 ++
 .../InteractionDtoVm.layout.xml}                   |   2 +-
 ...isherServiceToCaptureInteractionsInMemory.java} |   4 +-
 .../demoapp/dom/events/DemoEventSubscriber.java    |   2 +-
 .../java/demoapp/dom/homepage/DemoHomePage.java    |  13 +-
 .../src/main/java/demoapp/dom/menubars.layout.xml  |  62 +-
 .../TabMenu.java => services/ServicesMenu.java}    |  30 +-
 .../WrapperFactoryJdo-description.adoc             |   2 +
 .../services/wrapperFactory/WrapperFactoryJdo.java | 127 ++++
 .../wrapperFactory/WrapperFactoryJdo.layout.xml}   |  20 +-
 .../wrapperFactory/WrapperFactoryJdoEntities.java  |  33 ++
 .../WrapperFactoryJdoSeedService.java              |  39 ++
 ...WrapperFactoryJdo_mixinUpdatePropertyAsync.java |  44 ++
 ...WrapperFactoryJdo_updatePropertyAsyncMixin.java |  40 ++
 .../src/main/java/demoapp/dom/tabs/TabMenu.java    |   2 +-
 .../types/javalang/booleans/WrapperBooleans.java   |   2 +-
 .../dom/types/javalang/longs/WrapperLongs.java     |   2 +-
 .../jodatime/jodadatetime/vm/JodaDateTimeVm.java   |   9 +-
 .../jodatime/jodalocaldate/vm/JodaLocalDateVm.java |   9 +-
 .../jodalocaldatetime/vm/JodaLocalDateTimeVm.java  |   9 +-
 .../demo/domain/src/main/resources/application.yml |  10 +-
 .../config/application-primary.properties          |   1 +
 .../config/application-secondary.properties        |   6 +
 .../src/main/resources/static/css/application.css  |   3 +
 .../java/demoapp/webapp/vaadin/DemoAppVaadin.java  |   2 -
 examples/demo/web/pom.xml                          |  10 +
 .../src/main/java/demoapp/web/DemoAppManifest.java |  15 +-
 .../demoapp/web/replay/DemoReplayController.java   |  38 ++
 .../PrototypeActionsVisibilityAdvisor.java         |  63 ++
 .../java/demoapp/webapp/wicket/DemoAppWicket.java  |   7 +-
 .../wicket/src/main/resources/log4j2-spring.xml    |   9 +-
 .../adoc/modules/ROOT/partials/component-nav.adoc  |   1 +
 .../command-log/impl/logging-dn-enhance.properties |  29 +
 extensions/core/command-log/impl/pom.xml           |  75 +++
 .../impl/src/main/java/META-INF/persistence.xml    |  19 +
 .../impl/CommandServiceListenerForJdo.java         |  87 +++
 .../impl/IsisModuleExtCommandLogImpl.java          |  69 +++
 .../extensions/commandlog/impl/jdo/CommandJdo.java | 659 +++++++++++++++++++++
 .../impl/jdo/CommandJdo.layout.fallback.xml        | 138 +++++
 .../extensions/commandlog/impl/jdo/CommandJdo.png  | Bin 0 -> 582 bytes
 .../commandlog/impl/jdo/CommandJdoRepository.java  | 369 ++++++++++++
 .../impl/jdo/CommandJdo_childCommands.java         |  53 ++
 .../impl/jdo/CommandJdo_openResultObject.java      |  67 +++
 .../impl/jdo/CommandJdo_openTargetObject.java      |  67 +++
 .../commandlog/impl/jdo/CommandJdo_retry.java      |  68 +++
 .../impl/jdo/CommandJdo_siblingCommands.java       |  62 ++
 .../commandlog/impl/jdo/ReplayState.java           |  36 +-
 .../impl/mixins/HasUniqueId_command.java           |  61 ++
 .../mixins/HasUsername_recentCommandsByUser.java   |  45 ++
 .../impl/mixins/Object_recentCommands.java         |  59 ++
 .../commandlog/impl/mixins/T_recent.java           |  50 ++
 .../commandlog/impl/ui/CommandServiceMenu.java     | 120 ++++
 .../commandlog/impl/util/BigDecimalUtils.java      |  32 +
 .../commandlog/impl/util/StringUtils.java          |  17 +
 .../impl/util/StringUtils_trimmed_Test.java        |  28 +
 .../core/command-log}/pom.xml                      |  26 +-
 .../adoc/antora.yml                                |   2 +-
 .../adoc/modules/command-replay/nav.adoc           |  23 +-
 .../adoc/modules/command-replay/pages/about.adoc   |  24 +-
 .../command-replay/partials/module-nav.adoc        |   5 +
 .../core/command-replay}/pom.xml                   |  43 +-
 extensions/core/command-replay/primary/pom.xml     |  74 +++
 .../primary/IsisModuleExtCommandReplayPrimary.java |  35 ++
 .../primary/mixins/CommandJdo_download.java        |  48 ++
 .../primary/restapi/CommandRetrievalService.java   |  82 +++
 .../primary/spiimpl/CaptureResultOfCommand.java    |  61 ++
 .../primary/ui/CommandReplayOnPrimaryService.java  | 188 ++++++
 extensions/core/command-replay/secondary/pom.xml   |  80 +++
 .../IsisModuleExtCommandReplaySecondary.java       | 123 ++++
 .../commandreplay/secondary/SecondaryStatus.java   |  10 +
 .../commandreplay/secondary/StatusException.java   |  14 +
 .../secondary/analyser/CommandReplayAnalyser.java  |  14 +
 .../analyser/CommandReplayAnalyserException.java   |  71 +++
 .../analyser/CommandReplayAnalyserResult.java      |  58 ++
 .../analysis/CommandReplayAnalysisService.java     |  54 ++
 .../secondary/clock/TickingClockService.java       | 134 +++++
 .../secondary/config/SecondaryConfig.java          |  54 ++
 .../executor/CommandExecutorServiceWithTime.java   | 104 ++++
 .../secondary/fetch/CommandFetcher.java            | 153 +++++
 .../secondary/job/ReplicateAndReplayJob.java       |  88 +++
 .../secondary/job/SecondaryStatusData.java         |  36 ++
 .../jobcallables/IsTickingClockInitialized.java    |  22 +
 .../jobcallables/ReplicateAndRunCommands.java      | 171 ++++++
 .../secondary/mixins/CommandJdo_exclude.java       |  51 ++
 .../secondary/mixins/CommandJdo_openOnPrimary.java |  54 ++
 .../secondary/mixins/CommandJdo_replayQueue.java   |  42 ++
 .../spi/ReplayCommandExecutionController.java      |  25 +
 .../ui/CommandReplayOnSecondaryService.java        |  89 +++
 .../secondary/fetch/CommandFetcher_Test.java       |  35 ++
 .../flyway/adoc/modules/flyway/pages/about.adoc    |   2 +-
 extensions/core/model-annotation/adoc/antora.yml   |   2 +-
 .../adoc/modules/model-annotation/nav.adoc         |  23 +-
 .../adoc/modules/model-annotation/pages/about.adoc |  24 +-
 .../model-annotation/partials/module-nav.adoc      |   5 +
 .../{model-annotation => quartz}/adoc/antora.yml   |   0
 .../examples/DemoIsisInteractionTemplate.java      |  28 +
 .../adoc/modules/quartz/examples/DemoJob.java      |  37 ++
 .../examples/DemoJobQuartzConfigurerModule.java    |  69 +++
 .../core/quartz/adoc/modules/quartz/nav.adoc       |  23 +-
 .../quartz/adoc/modules/quartz/pages/about.adoc    |  38 +-
 .../adoc/modules/quartz/partials/module-nav.adoc   |   5 +
 extensions/core/quartz/impl/pom.xml                |  72 +++
 .../extensions/quartz/IsisModuleExtQuartzImpl.java |  15 +
 .../quartz/context/JobExecutionData.java           |  36 ++
 .../spring/AutowiringSpringBeanJobFactory.java     |  30 +
 .../restclient => extensions/core/quartz}/pom.xml  |  19 +-
 extensions/pom.xml                                 |  30 +-
 .../ui/auth/VaadinAuthenticationHandler.java       |   2 +-
 .../isis/legacy/applib/DomainObjectContainer.java  |   3 +
 .../background/BackgroundCommandService2.java      |  30 -
 .../services/background/BackgroundService.java     |  60 --
 .../services/background/BackgroundService2.java    |  28 -
 .../services/eventbus/ActionDomainEvent.java       |   9 +-
 .../services/eventbus/CollectionDomainEvent.java   |   3 +
 .../services/eventbus/PropertyDomainEvent.java     |   4 +
 .../org/apache/isis/legacy/applib/value/Color.java |   3 +
 .../apache/isis/legacy/applib/value/Magnitude.java |   4 +
 .../org/apache/isis/legacy/applib/value/Money.java |   4 +
 .../isis/legacy/applib/value/Percentage.java       |   4 +
 mappings/jaxrsclient/{api => applib}/build.gradle  |   2 +-
 mappings/jaxrsclient/{api => applib}/pom.xml       |  10 +-
 .../applib/IsisModuleExtJaxRsClientApplib.java}    |  13 +-
 .../jaxrsclient/applib/client/JaxRsClient.java}    |  38 +-
 .../jaxrsclient/applib/client/JaxRsResponse.java}  |  10 +-
 mappings/jaxrsclient/{api => impl}/pom.xml         |  19 +-
 .../impl/IsisModuleExtJaxRsClientImpl.java}        |   4 +-
 .../impl/client/JaxRsClientDefault.java            | 136 +++++
 .../impl/client/JaxRsResponseDefault.java          |  24 +
 mappings/jaxrsclient/pom.xml                       |   3 +-
 mappings/jaxrsclient/{api => testlib}/pom.xml      |  13 +-
 .../impl/IsisModuleExtJaxRsClientImpl.java}        |   4 +-
 .../jaxrsclient/impl/JaxRsClientDefault.java       | 148 +++++
 .../extensions/jaxrsclient/impl/JaxRsResponse.java |  54 ++
 mappings/pom.xml                                   |  10 +-
 mappings/restclient/{api => applib}/build.gradle   |   2 +-
 mappings/restclient/{api => applib}/pom.xml        |   4 +-
 .../restclient/ActionParameterListBuilder.java     |   0
 .../restclient/IsisModuleExtRestClient.java        |   0
 .../isis/extensions/restclient/ResponseDigest.java |   0
 .../isis/extensions/restclient/RestfulClient.java  |   0
 .../extensions/restclient/RestfulClientConfig.java |   0
 .../restclient/RestfulClientException.java         |   0
 .../isis/extensions/restclient/ScalarValueDto.java |   0
 .../restclient/auth/BasicAuthFilter.java           |   0
 .../restclient/log/RestfulLoggingFilter.java       |   0
 mappings/restclient/pom.xml                        |   2 +-
 .../service/JdoPersistenceLifecycleService.java    |  21 +-
 .../IsisBookmarkConverter.java}                    |  17 +-
 .../IsisLocalResourcePathConverter.java            |   2 +-
 .../{ => applib}/IsisMarkupConverter.java          |   2 +-
 .../{ => applib}/IsisPasswordConverter.java        |   2 +-
 .../v2/IsisChangesDtoConverter.java}               |  16 +-
 .../v2/IsisCommandDtoConverter.java}               |  17 +-
 .../v2/IsisInteractionDtoConverter.java}           |  14 +-
 .../v2/IsisOidDtoConverter.java}                   |  21 +-
 .../{ => applib}/ByteArrayBlobRdbmsMapping.java    |   2 +-
 .../valuetypes/{ => applib}/IsisBlobMapping.java   |   2 +-
 .../valuetypes/{ => applib}/IsisClobMapping.java   |   2 +-
 .../persistence/IsisPersistenceSessionJdoBase.java |   5 +-
 .../IsisPlatformTransactionManagerForJdo.java      |  11 +-
 .../persistence/IsisTransactionManagerJdo.java     |  39 +-
 .../persistence/PersistenceSession5.java           |  93 +--
 .../datanucleus-5/src/main/resources/plugin.xml    |  96 ++-
 regressiontests/pom.xml                            |   2 +-
 .../testdomain/conf/Configuration_headless.java    |  22 +-
 .../bootstrapping/builtin-requestscoped.list       |   1 -
 .../applib/teardown/TeardownFixtureAbstract.java   |  91 ++-
 .../adoc/modules/integtestsupport/pages/about.adoc |   2 +-
 testing/integtestsupport/applib/pom.xml            |  57 ++
 .../applib/IsisIntegrationTestAbstract.java        |  37 +-
 .../applib/IsisInteractionHandler.java             |   2 +-
 tooling/javamodel/pom.xml                          |   1 +
 tooling/projectmodel/pom.xml                       |   1 +
 valuetypes/pom.xml                                 |   5 -
 .../resources/DomainObjectResourceServerside.java  |  24 +-
 .../resources/DomainServiceResourceServerside.java |  21 +-
 .../viewer/resources/ObjectActionArgHelper.java    |  10 +-
 .../viewer/resources/ResourceAbstract.java         |   9 -
 .../wicket/ui/app/logout/LogoutHandlerWkt.java     |   6 +-
 .../CollectionContentsAsAjaxTablePanel.java        |   1 -
 .../components/scalars/bookmark/BookmarkPanel.java |  26 +-
 .../scalars/bookmark/BookmarkPanelFactory.java     |  28 +-
 .../scalars/markup/MarkupPanelFactories.java       |   5 +-
 .../ui/components/scalars/oiddto/OidDtoPanel.java  |  26 +-
 .../scalars/oiddto/OidDtoPanelFactory.java         |  25 +-
 .../isis/viewer/wicket/ui/errors/JGrowlUtil.java   |   4 +-
 .../wicket/ui/panels/FormExecutorDefault.java      |  18 +-
 .../wicket/viewer/mixins/Object_clearHints.java    |  36 +-
 .../ComponentFactoryRegistrarDefault.java          |   7 +
 427 files changed, 9813 insertions(+), 6925 deletions(-)

diff --git a/antora/playbooks/site-extensions.yml b/antora/playbooks/site-extensions.yml
index 9d17401..c4aef14 100644
--- a/antora/playbooks/site-extensions.yml
+++ b/antora/playbooks/site-extensions.yml
@@ -44,8 +44,14 @@ content:
       start_path: extensions/core/command-log/adoc # extensions
       branches: HEAD
     - url: .
+      start_path: extensions/core/command-replay/adoc # extensions
+      branches: HEAD
+    - url: .
       start_path: extensions/core/flyway/adoc # userguide
       branches: HEAD
+    - url: .
+      start_path: extensions/core/model-annotation/adoc # extensions
+      branches: HEAD
 
     - url: .
       start_path: extensions/security/secman/adoc # security
diff --git a/antora/playbooks/site.yml b/antora/playbooks/site.yml
index 9e56fcd..b4473fb 100644
--- a/antora/playbooks/site.yml
+++ b/antora/playbooks/site.yml
@@ -109,8 +109,17 @@ content:
       start_path: extensions/core/command-log/adoc # extensions
       branches: HEAD
     - url: .
+      start_path: extensions/core/command-replay/adoc # extensions
+      branches: HEAD
+    - url: .
       start_path: extensions/core/flyway/adoc # userguide
       branches: HEAD
+    - url: .
+      start_path: extensions/core/model-annotation/adoc # extensions
+      branches: HEAD
+    - url: .
+      start_path: extensions/core/quartz/adoc # userguide
+      branches: HEAD
 
     - url: .
       start_path: extensions/security/audit-trail/adoc # security
diff --git a/api/adoc/pom.xml b/api/adoc/pom.xml
new file mode 100644
index 0000000..3749db1
--- /dev/null
+++ b/api/adoc/pom.xml
@@ -0,0 +1,40 @@
+<?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.isis.core</groupId>
+        <artifactId>isis-core</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+        <relativePath>../../core/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>isis-api-adoc</artifactId>
+
+    <name>Apache Isis Core - API Documentation</name>
+    <description>
+        User guide and reference guide.
+        This pom.xml is just for convenience to allow the documentation to be
+        opened independently of the entire project.  It is not part of the
+        main hierarchy.
+    </description>
+
+</project>
diff --git a/api/adoc/userguide/modules/ROOT/partials/component-nav.adoc b/api/adoc/userguide/modules/ROOT/partials/component-nav.adoc
index f35ad96..227dd73 100644
--- a/api/adoc/userguide/modules/ROOT/partials/component-nav.adoc
+++ b/api/adoc/userguide/modules/ROOT/partials/component-nav.adoc
@@ -4,5 +4,6 @@ include::userguide:btb:partial$module-nav.adoc[]
 * Extensions
 
 include::userguide:flyway:partial$module-nav.adoc[]
+include::userguide:quartz:partial$module-nav.adoc[]
 
 
diff --git a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Action.java b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Action.java
index c73d945..f8c0703 100644
--- a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Action.java
+++ b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Action.java
@@ -26,11 +26,10 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 import org.apache.isis.applib.events.domain.ActionDomainEvent;
-import org.apache.isis.applib.services.command.CommandDtoProcessor;
-import org.apache.isis.applib.services.command.CommandWithDto;
-import org.apache.isis.applib.services.command.spi.CommandService;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandDto;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandsDto;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
+import org.apache.isis.applib.services.command.CommandService;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
 import org.apache.isis.applib.value.Blob;
 import org.apache.isis.applib.value.Clob;
 
@@ -99,29 +98,6 @@ public @interface Action {
 
     // end::refguide[]
     /**
-     * How the {@link org.apache.isis.applib.services.command.Command Command} object provided by the
-     * {@link org.apache.isis.applib.services.command.CommandContext CommandContext} domain service should be persisted.
-     */
-    // tag::refguide[]
-    CommandPersistence commandPersistence()                         // <.>
-            default CommandPersistence.PERSISTED;
-
-    // end::refguide[]
-    /**
-     * How the command/action should be executed.
-     *
-     * <p>
-     * If the corresponding {@link org.apache.isis.applib.services.command.Command Command} object is persisted,
-     * then its {@link org.apache.isis.applib.services.command.Command#getExecuteIn() invocationType} property
-     * will be set to this value.
-     * </p>
-     */
-    // tag::refguide[]
-    CommandExecuteIn commandExecuteIn()                             // <.>
-            default CommandExecuteIn.FOREGROUND;
-
-    // end::refguide[]
-    /**
      * The {@link CommandDtoProcessor} to process this command's DTO.
      *
      * <p>
diff --git a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/CommandExecuteIn.java b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/CommandExecuteIn.java
deleted file mode 100644
index 14afaed..0000000
--- a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/CommandExecuteIn.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.annotation;
-
-/**
- * Whether a command should be executed immediately and synchronously in the foreground or rather should only be
- * persisted (such that it can be executed asynchronously in the background by some other mechanism).
- *
- * <p>
- *     Note: this enum is <i>not</i> an inner class of the {@link org.apache.isis.applib.annotation.Action} annotation
- *     because in the future we may also support commands for {@link org.apache.isis.applib.annotation.Property} and
- *     {@link org.apache.isis.applib.annotation.Collection}.
- * </p>
- */
-// tag::refguide[]
-public enum CommandExecuteIn {
-    // end::refguide[]
-    /**
-     * Execute synchronously in the &quot;foreground&quot;, wait for the results.
-     */
-    // tag::refguide[]
-    FOREGROUND,
-    // end::refguide[]
-    /**
-     * Execute &quot;asynchronously&quot; through the {@link org.apache.isis.applib.services.background.BackgroundCommandService}, returning (if possible) the
-     * persisted {@link org.apache.isis.applib.services.command.Command command} object as a placeholder to the
-     * result.
-     */
-    // tag::refguide[]
-    BACKGROUND,
-    // end::refguide[]
-    /**
-     * For commands that are replicated from a master onto a slave and are to be replayed (typically using the same
-     * mechanism as "regular" background commands, eg a background job).
-     *
-     * <p>
-     *     For framework use, not intended to be used in application code.
-     * </p>
-     */
-    // tag::refguide[]
-    REPLAYABLE,
-    // end::refguide[]
-    /**
-     * For commands that have been excluded and will not run.
-     * These are typically for a replayable command that has hit an exception (which normally would prevent any further
-     * replayable commands from being replayed) and which the administrator has decided to skip.
-     */
-    // tag::refguide[]
-    EXCLUDED
-    // end::refguide[]
-    ;
-
-    public boolean isForeground() { return this == FOREGROUND; }
-    public boolean isBackground() { return this == BACKGROUND; }
-    public boolean isReplayable() { return this == REPLAYABLE; }
-    public boolean isExcluded() { return this == EXCLUDED; }
-
-    public static class Type {
-        private Type() {}
-        public static class Meta {
-            public static final int MAX_LEN = 10;
-            private Meta() {}
-        }
-    }
-    // tag::refguide[]
-}
-// end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/CommandPersistence.java b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/CommandPersistence.java
deleted file mode 100644
index 7f7000a..0000000
--- a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/CommandPersistence.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.annotation;
-
-/**
- * Whether the command should be persisted.
- */
-// tag::refguide[]
-public enum CommandPersistence {
-
-    // end::refguide[]
-    /**
-     * (If the configured {@link org.apache.isis.applib.services.command.spi.CommandService} supports it), indicates that the
-     * {@link org.apache.isis.applib.services.command.Command Command} object should be persisted.
-     */
-    // tag::refguide[]
-    PERSISTED,
-    // end::refguide[]
-    /**
-     * (If the configured {@link org.apache.isis.applib.services.command.spi.CommandService} supports it), indicates that the
-     * {@link org.apache.isis.applib.services.command.Command Command} object should only be persisted if
-     * another service, such as the {@link org.apache.isis.applib.services.background.BackgroundCommandService}, hints that it should.
-     */
-    // tag::refguide[]
-    IF_HINTED,
-    // end::refguide[]
-    /**
-     * (Even if the configured {@link org.apache.isis.applib.services.command.spi.CommandService} supports it), indicates that the
-     * {@link org.apache.isis.applib.services.command.Command Command} object should <i>not</i> be persisted (even if
-     * another service, such as the {@link org.apache.isis.applib.services.background.BackgroundCommandService}, hints that it should).
-     */
-    // tag::refguide[]
-    NOT_PERSISTED
-
-}
-// end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Nature.java b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Nature.java
index b044747..8ac77a7 100644
--- a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Nature.java
+++ b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Nature.java
@@ -55,6 +55,22 @@ public enum Nature {
      */
     // tag::refguide[]
     JDO_ENTITY,
+    
+    // end::refguide[]
+    /**
+     * A domain entity whose persistence is managed internally by Isis, using JPA as the persistence implementation.
+     * Domain entities are considered to be part of the domain model layer.
+     *
+     * <p>
+     *     Domain entities are considered to be part of the domain model layer.
+     * </p>
+     *
+     * <p>
+     *    Currently implies no additional semantics other than documentation.
+     * </p>
+     */
+    // tag::refguide[]
+    JPA_ENTITY,
 
     // end::refguide[]
     /**
diff --git a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Property.java b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Property.java
index ac40f2d..65db374 100644
--- a/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Property.java
+++ b/api/applib/src/main/adoc/modules/applib-ant/examples/annotation/Property.java
@@ -26,11 +26,9 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 import org.apache.isis.applib.events.domain.PropertyDomainEvent;
-import org.apache.isis.applib.services.command.CommandDtoProcessor;
-import org.apache.isis.applib.services.command.CommandWithDto;
-import org.apache.isis.applib.services.command.spi.CommandService;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandDto;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandsDto;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
 import org.apache.isis.applib.spec.Specification;
 import org.apache.isis.applib.value.Blob;
 import org.apache.isis.applib.value.Clob;
@@ -52,7 +50,8 @@ public @interface Property {
 
     // end::refguide[]
     /**
-     * Whether the property edit should be reified into a {@link org.apache.isis.applib.services.command.Command} object.
+     * Whether the property edit should be reified into a
+     * {@link org.apache.isis.applib.services.command.Command} object.
      */
     // tag::refguide[]
     CommandReification command()                                // <.>
@@ -60,39 +59,10 @@ public @interface Property {
 
     // end::refguide[]
     /**
-     * How the {@link org.apache.isis.applib.services.command.Command Command} object provided by the
-     * {@link org.apache.isis.applib.services.command.CommandContext CommandContext} domain service should be persisted.
-     */
-    // tag::refguide[]
-    CommandPersistence commandPersistence()                     // <.>
-            default CommandPersistence.PERSISTED;
-
-    // end::refguide[]
-    /**
-     * How the command/property edit should be executed.
-     *
-     * <p>
-     * If the corresponding {@link org.apache.isis.applib.services.command.Command Command} object is persisted,
-     * then its {@link org.apache.isis.applib.services.command.Command#getExecuteIn() invocationType} property
-     * will be set to this value.
-     * </p>
-     */
-    // tag::refguide[]
-    CommandExecuteIn commandExecuteIn()                         // <.>
-            default CommandExecuteIn.FOREGROUND;
-
-    // end::refguide[]
-    /**
      * The {@link CommandDtoProcessor} to process this command's DTO.
      *
      * <p>
-     *     Specifying a processor requires that the implementation of {@link CommandService} provides a
-     *     custom implementation of {@link org.apache.isis.applib.services.command.Command} that additional extends
-     *     from {@link CommandWithDto}.
-     * </p>
-     *
-     * <p>
-     *     Tprocessor itself is used by {@link ContentMappingServiceForCommandDto} and
+     *     The processor itself is used by {@link ContentMappingServiceForCommandDto} and
      *     {@link ContentMappingServiceForCommandsDto} to dynamically transform the DTOs.
      * </p>
      */
@@ -210,7 +180,7 @@ public @interface Property {
      *
      * <p>
      *     For properties the default value, {@link org.apache.isis.applib.annotation.Optionality#DEFAULT}, usually
-     *     means that the property is required unless it has been overridden by {@link javax.jdo.annotations.Column}
+     *     means that the property is required unless it has been overridden by <code>javax.jdo.annotations.Column</code>
      *     with its <code>javax.jdo.annotations.Column#allowsNull()</code> attribute set to true.
      * </p>
      */
diff --git a/api/applib/src/main/adoc/modules/applib-ant/pages/Action.adoc b/api/applib/src/main/adoc/modules/applib-ant/pages/Action.adoc
index 46a6dc4..4d2cb68 100644
--- a/api/applib/src/main/adoc/modules/applib-ant/pages/Action.adoc
+++ b/api/applib/src/main/adoc/modules/applib-ant/pages/Action.adoc
@@ -28,7 +28,7 @@ Whether to execute the command immediately, or to persist it (assuming that an a
 
 <.> `commandPersistence()`
 +
-Whether the reified `Command` (as provided by the `CommandContext` domain service) should actually be persisted (assuming an appropriate implementation of xref:refguide:applib-svc:CommandService.adoc[`CommandService`] has been configured).
+Whether the reified `Command` (obtainable from the `InteractionContext` domain service) should actually be persisted (assuming an appropriate implementation of xref:refguide:applib-svc:CommandService.adoc[`CommandService`] has been configured).
 
 <.> `commandDtoProcessor()`
 +
diff --git a/api/applib/src/main/adoc/modules/applib-ant/pages/Action/command.adoc b/api/applib/src/main/adoc/modules/applib-ant/pages/Action/command.adoc
index 5a35003..b30b43d 100644
--- a/api/applib/src/main/adoc/modules/applib-ant/pages/Action/command.adoc
+++ b/api/applib/src/main/adoc/modules/applib-ant/pages/Action/command.adoc
@@ -4,6 +4,7 @@
 :Notice: 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 ag [...]
 :page-partial:
 
+CAUTION: TODO - v2, this documentation is not out of date.
 
 Every action invocation (and property edit for that matter) is automatically reified into a concrete `Command` object.
 The `@Action(command=..., commandXxx=...)` attributes provide hints for the persistence of that `Command` object, and the subsequent processing of that persisted command.
@@ -277,25 +278,6 @@ For persisted commands, the `commandExecuteIn` attribute determines whether the
 
 WARNING: TODO: v2 - background execution has temporarily been removed, will be reinstated for v2.0.
 
-//Background execution means that the command is not executed immediately, but is available for a configured xref:refguide:applib-svc:_BackgroundCommandService.adoc[`BackgroundCommandService`] to execute, eg by way of an in-memory scheduler such as Quartz.
-//See xref:userguide:btb:about.adoc#BackgroundCommandExecution[here] for further information on this topic.
-
-//For example:
-//
-//[source,java]
-//----
-//public class Order {
-//    @Action(
-//        command=CommandReification.ENABLED,
-//        commandExecuteIn=CommandExecuteIn.BACKGROUND
-//    )
-//    public Invoice generateInvoice(...) { /* ... */ }
-//}
-//----
-//
-//will result in the `Command` being persisted but its execution deferred to a background execution mechanism.
-//The returned object from this action invocation is the persisted `Command` itself.
-
 
 
 
@@ -307,15 +289,13 @@ This interface has the following API:
 [source,java]
 ----
 public interface CommandDtoProcessor {
-    CommandDto process(             // <1>
-            Command command,        // <2>
-            CommandDto dto);        // <3>
+    CommandDto process(             // <.>
+            CommandDto dto);        // <.>
 }
 ----
-<1> The returned `CommandDto`.
-This will typically be the `CommandDto` passed in, but supplemented in some way.
-<2> The `Command` being processed
-<3> The `CommandDto` (XML) obtained already from the `Command` (by virtue of it also implementing `CommandWithDto`, see discussion below).
+<.> The returned `CommandDto`.
+This will typically be the `CommandDto` passed in, but may be supplemented in some way.
+<.> The `CommandDto` obtained already from the `Command`.
 
 This interface is used by the framework-provided implementations of `ContentMappingService` for the REST API, allowing ``Command``s implementations that also implement `CommandWithDto` to be further customised as they are serialized out.
 The primary use case for this capability is in support of master/slave replication.
diff --git a/api/applib/src/main/adoc/modules/applib-ant/pages/Property.adoc b/api/applib/src/main/adoc/modules/applib-ant/pages/Property.adoc
index 7df9341..4171f0e 100644
--- a/api/applib/src/main/adoc/modules/applib-ant/pages/Property.adoc
+++ b/api/applib/src/main/adoc/modules/applib-ant/pages/Property.adoc
@@ -3,6 +3,7 @@
 
 :Notice: 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 ag [...]
 
+CAUTION: TODO: v2 - command annotations are out-of-date
 
 The `@Property` annotation applies to properties collecting together all domain semantics within a single annotation.
 
diff --git a/api/applib/src/main/adoc/modules/applib-ant/pages/Property/command.adoc b/api/applib/src/main/adoc/modules/applib-ant/pages/Property/command.adoc
index 7562660..40f23a3 100644
--- a/api/applib/src/main/adoc/modules/applib-ant/pages/Property/command.adoc
+++ b/api/applib/src/main/adoc/modules/applib-ant/pages/Property/command.adoc
@@ -4,6 +4,7 @@
 :Notice: 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 ag [...]
 :page-partial:
 
+CAUTION: TODO - v2 to update, has been simplified.
 
 Every property edit (and action invocation for that matter) is automatically reified into a concrete `Command` object.
 The `@Property(command=..., commandXxx=...)` attributes provide hints for the persistence of that `Command` object, and the subsequent processing of that persisted command.
@@ -16,8 +17,6 @@ The annotation works with (and is influenced by the behaviour of) a number of do
 
 * xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`]
 * xref:refguide:applib-svc:CommandService.adoc[`CommandService`]
-//* xref:refguide:applib-svc:BackgroundService.adoc[`BackgroundService`] and
-//* xref:refguide:applib-svc:_BackgroundCommandService.adoc[`BackgroundCommandService`]
 
 
 Each property edit is automatically reified by the xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`] service into a `Command` object, capturing details of the target object, the property, the proposed new value for the property, the user, a timestamp and so on.
@@ -25,197 +24,6 @@ Each property edit is automatically reified by the xref:refguide:applib-svc:Comm
 If an appropriate `CommandService` is configured (for example using xref:extensions:command-log:about.adoc[Command Log] extension module), then the `Command` itself is persisted.
 
 By default, actions are invoked in directly in the thread of the invocation.
-//If there is an implementation of `BackgroundCommandService` (as the (non-ASF) link:https://platform.incode.org[Incode Platform^]'s command module does provide), then this means in turn that the `BackgroundService` can be used by the domain object code to programmatically create background ``Command``s.
-
-//[NOTE]
-//====
-//If background ``Command``s are used, then an external scheduler, using xref:userguide:btb:about.adoc#BackgroundCommandExecution[headless access], must also be configured.
-//====
-
-== `command()` and `commandPersistence()`
-
-The `command()` and `commandPersistence() attributes work together to determine whether a command will actually be persisted.
-There inter-relationship is somewhat complex, so is probably best explained by way of examples:
-
-[cols="1a,1a,1a,1a,2a", options="header"]
-|===
-
-| `command()`
-|`isis.services.
-command.properties` config property
-| `command
-Persistence()`
-| action dirties objects?
-| is command persisted?
-
-| `ENABLED`
-| (any)
-| `PERSISTED`
-| (either)
-| yes
-
-| `ENABLED`
-| (any)
-| `IF_HINTED`
-| no
-| no
-
-| `ENABLED`
-| (any)
-| `IF_HINTED`
-| yes
-| yes
-
-| `ENABLED`
-| (any)
-| `NOT_PERSISTED`
-| (any)
-| no
-
-| `AS_CONFIGURED`
-| `all`
-| `PERSISTED`
-| no
-| yes
-
-| `AS_CONFIGURED`
-| `all`
-| `IF_HINTED`
-| no
-| no
-
-| `AS_CONFIGURED`
-| `all`
-| `IF_HINTED`
-| yes
-| yes
-
-| `AS_CONFIGURED`
-| `all`
-| `NOT_PERSISTED`
-| (any)
-| no
-
-| `AS_CONFIGURED`
-| `none`
-| `PERSISTED`
-| no
-| no (!)
-
-| `AS_CONFIGURED`
-| `none`
-| `PERSISTED`
-| yes
-| yes
-
-| `AS_CONFIGURED`
-| `none`
-| `IF_HINTED`
-| no
-| no
-
-| `AS_CONFIGURED`
-| `none`
-| `IF_HINTED`
-| yes
-| yes
-
-| `AS_CONFIGURED`
-| `none`
-| `NOT_PERSISTED`
-| no
-| no
-
-| `AS_CONFIGURED`
-| `none`
-| `NOT_PERSISTED`
-| yes
-| yes (!)
-
-| `DISABLED`
-| (any)
-| `PERSISTED`
-| no
-| no (!)
-
-| `DISABLED`
-| (any)
-| `PERSISTED`
-| yes
-| yes
-
-| `DISABLED`
-| (any)
-| `IF_HINTED`
-| no
-| no
-
-| `DISABLED`
-| (any)
-| `IF_HINTED`
-| yes
-| yes
-
-| `DISABLED`
-| (any)
-| `NOT_PERSISTED`
-| no
-| no
-
-| `DISABLED`
-| (any)
-| `NOT_PERSISTED`
-| yes
-| yes (!)
-
-|===
-
-For example:
-
-[source,java]
-----
-public class Order {
-    @Property(
-        command=CommandReification.ENABLED,
-        commandPersistence=CommandPersistence.PERSISTED
-    )
-    public Product getProduct() { /* ... */ }
-    public void setProduct(Product p) { /* ... */ }
-}
-----
-
-As can be seen, whether a command is actually persisted does not always follow the value of the `commandPersistence` attribute.
-This is because the `command` attribute actually determines whether any command metadata for the action is captured within the framework's internal metamodel.
-If `command` is `DISABLED` or does not otherwise apply due to the action's declared semantics, then the framework decides to persist a command based solely on whether the action dirtied any objects (as if `commandPersistence()` was set to `IF_HINTED`).
-
-
-
-== `commandExecuteIn()`
-
-For persisted commands, the `commandExecuteIn` attribute determines whether the `Command` should be executed in the foreground (the default) or executed in the background.
-
-WARNING: TODO: v2 - background execution has temporarily been removed, will be reinstated for v2.0.
-
-//Background execution means that the command is not executed immediately, but is available for a configured xref:refguide:applib-svc:_BackgroundCommandService.adoc[`BackgroundCommandService`] to execute, eg by way of an in-memory scheduler such as Quartz.
-//See xref:userguide:btb:about.adoc#BackgroundCommandExecution[here] for further information on this topic.
-
-//For example:
-//
-//[source,java]
-//----
-//public class Order {
-//    @Property(
-//        command=CommandReification.ENABLED,
-//        commandExecuteIn=CommandExecuteIn.BACKGROUND
-//    )
-//    public Product getProduct() { /* ... */ }
-//    public void setProduct(Product p) { /* ... */ }
-//}
-//----
-//
-//will result in the `Command` being persisted but its execution deferred to a background execution mechanism.
-//The returned object from this property edit is the persisted `Command` itself.
-
 
 
 
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/domain/DomainObjectList.java b/api/applib/src/main/adoc/modules/applib-classes/examples/domain/DomainObjectList.java
index 2a13734..7dfe76a 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/domain/DomainObjectList.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/domain/DomainObjectList.java
@@ -35,7 +35,7 @@ import org.apache.isis.applib.annotation.Editing;
 import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.annotation.Optionality;
 import org.apache.isis.applib.annotation.Property;
-import org.apache.isis.applib.jaxbadapters.PersistentEntitiesAdapter;
+import org.apache.isis.applib.jaxb.PersistentEntitiesAdapter;
 
 @XmlRootElement(name = "list")
 @XmlType(
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/AbstractDomainEvent.java b/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/AbstractDomainEvent.java
index 2ddd967..0709768 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/AbstractDomainEvent.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/AbstractDomainEvent.java
@@ -22,9 +22,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.isis.applib.Identifier;
-import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.events.EventObjectBase;
-import org.apache.isis.applib.services.command.CommandContext;
 import org.apache.isis.applib.services.i18n.TranslatableString;
 import org.apache.isis.applib.util.ObjectContracts;
 import org.apache.isis.applib.util.ToString;
@@ -108,7 +106,7 @@ public abstract class AbstractDomainEvent<S> extends EventObjectBase<S> {
 
         /**
          * When the {@link org.apache.isis.applib.services.command.Command} is made available on the
-         * {@link org.apache.isis.applib.events.domain.ActionDomainEvent} via {@link CommandContext#getCommand()}.
+         * {@link org.apache.isis.applib.events.domain.ActionDomainEvent} via {@link org.apache.isis.applib.services.iactn.Interaction#getCommand()}.
          */
         public boolean isExecutingOrLater() {
             return isExecuting() || isExecuted();
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/ActionDomainEvent.java b/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/ActionDomainEvent.java
index b935e91..f891fe1 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/ActionDomainEvent.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/ActionDomainEvent.java
@@ -25,6 +25,7 @@ import org.apache.isis.applib.util.ObjectContracts;
 import org.apache.isis.applib.util.ToString;
 
 import lombok.Getter;
+import lombok.Setter;
 
 // tag::refguide[]
 public abstract class ActionDomainEvent<S> extends AbstractDomainEvent<S> {
@@ -110,9 +111,16 @@ public abstract class ActionDomainEvent<S> extends AbstractDomainEvent<S> {
     /**
      * The arguments being used to invoke the action; populated at {@link Phase#VALIDATE} and subsequent phases
      * (but null for {@link Phase#HIDE hidden} and {@link Phase#DISABLE disable} phases).
+     *
+     * <p>
+     *     The argument values can also be modified by event handlders
+     *     during the {@link Phase#EXECUTING} phase.    The new value must be
+     *     the same type as the expected value; the framework performs
+     *     no sanity checks.
+     * </p>
      */
     // tag::refguide[]
-    @Getter
+    @Getter @Setter
     private List<Object> arguments;
 
     // end::refguide[]
@@ -166,12 +174,6 @@ public abstract class ActionDomainEvent<S> extends AbstractDomainEvent<S> {
     public void setMixedIn(final Object mixedIn) {
         this.mixedIn = mixedIn;
     }
-    /**
-     * Not API - set by the framework.
-     */
-    public void setArguments(List<Object> arguments) {
-        this.arguments = arguments;
-    }
 
 
     private static final ToString<ActionDomainEvent<?>> toString = ObjectContracts.<ActionDomainEvent<?>>
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/PropertyDomainEvent.java b/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/PropertyDomainEvent.java
index 405f4a5..1dbf127 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/PropertyDomainEvent.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/events/domain/PropertyDomainEvent.java
@@ -22,6 +22,7 @@ import org.apache.isis.applib.util.ObjectContracts;
 import org.apache.isis.applib.util.ToString;
 
 import lombok.Getter;
+import lombok.Setter;
 
 // tag::refguide[]
 public abstract class PropertyDomainEvent<S,T> extends AbstractDomainEvent<S> {
@@ -69,32 +70,27 @@ public abstract class PropertyDomainEvent<S,T> extends AbstractDomainEvent<S> {
      * (but null for {@link org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase#HIDE hidden} and {@link org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase#DISABLE disable} phases).
      */
     // tag::refguide[]
-    @Getter
+    @Getter @Setter
     private T oldValue;
 
     // end::refguide[]
     /**
      * The proposed (post-modification) value of the property; populated at {@link org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase#VALIDATE} and subsequent phases
      * (but null for {@link org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase#HIDE hidden} and {@link org.apache.isis.applib.events.domain.AbstractDomainEvent.Phase#DISABLE disable} phases).
+     *
+     * <p>
+     *     The proposed new value can also be modified by event handlers
+     *     during the {@link Phase#EXECUTING} phase.  The new value must be
+     *     the same type as the expected value; the framework performs
+     *     no sanity checks.
+     * </p>
      */
     // tag::refguide[]
-    @Getter
+    @Getter @Setter
     private T newValue;
 
     // end::refguide[]
-    /**
-     * Not API; for framework use only.
-     */
-    public void setOldValue(T oldValue) {
-        this.oldValue = oldValue;
-    }
 
-    /**
-     * Not API; for framework use only.
-     */
-    public void setNewValue(T newValue) {
-        this.newValue = newValue;
-    }
 
 
     private static final ToString<PropertyDomainEvent<?,?>> toString =
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/layout/component/PropertyLayoutData.java b/api/applib/src/main/adoc/modules/applib-classes/examples/layout/component/PropertyLayoutData.java
index 817a6aa..a56700b 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/layout/component/PropertyLayoutData.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/layout/component/PropertyLayoutData.java
@@ -211,18 +211,6 @@ HasCssClass, HasDescribedAs, HasHidden, HasNamed  {
     }
 
 
-    @XmlAttribute(required = false)
-    public Boolean getUnchanging() {
-        return getRepainting() != null ? getRepainting() == Repainting.NO_REPAINT : null;
-    }
-
-    public void setUnchanging(Boolean unchanging) {
-        if(getRepainting() == null && unchanging != null) {
-            setRepainting(unchanging ? Repainting.NO_REPAINT : Repainting.REPAINT);
-        }
-    }
-
-
     private Repainting repainting;
 
     @XmlAttribute(required = false)
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/Menu.java b/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/HasNamed.java
similarity index 96%
copy from api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/Menu.java
copy to api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/HasNamed.java
index cbb2797..6627d86 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/Menu.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/HasNamed.java
@@ -20,9 +20,9 @@ package org.apache.isis.applib.layout.menubars;
 
 import org.apache.isis.applib.annotation.Programmatic;
 
-public interface Menu {
+public interface HasNamed {
 
     @Programmatic
     String getNamed();
-
+    
 }
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/Menu.java b/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/Menu.java
index cbb2797..68789ba 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/Menu.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/Menu.java
@@ -18,11 +18,6 @@
  */
 package org.apache.isis.applib.layout.menubars;
 
-import org.apache.isis.applib.annotation.Programmatic;
-
-public interface Menu {
-
-    @Programmatic
-    String getNamed();
+public interface Menu extends HasNamed {
 
 }
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/MenuSection.java b/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/MenuSection.java
index d3f2873..feacaf0 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/MenuSection.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/MenuSection.java
@@ -23,7 +23,7 @@ import java.util.List;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.layout.component.ServiceActionLayoutData;
 
-public interface MenuSection {
+public interface MenuSection extends HasNamed {
 
     @Programmatic
     List<ServiceActionLayoutData> getServiceActions();
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/bootstrap3/BS3MenuSection.java b/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/bootstrap3/BS3MenuSection.java
index 79716b6..1e563b8 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/bootstrap3/BS3MenuSection.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/layout/menubars/bootstrap3/BS3MenuSection.java
@@ -36,6 +36,7 @@ import org.apache.isis.applib.layout.menubars.MenuSection;
 @XmlType(
         name = "section"
         , propOrder = {
+                "named",
                 "serviceActions"
         }
         )
@@ -46,7 +47,18 @@ public class BS3MenuSection implements MenuSection, Serializable, ServiceActionL
     public BS3MenuSection() {
     }
 
+    private String named;
 
+    @Override
+    @XmlElement(required = false)
+    public String getNamed() {
+        return named;
+    }
+
+    public void setNamed(String named) {
+        this.named = named;
+    }
+    
     private List<ServiceActionLayoutData> serviceActions = new ArrayList<>();
 
     // no wrapper
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/mixins/layout/Object_rebuildMetamodel.java b/api/applib/src/main/adoc/modules/applib-classes/examples/mixins/layout/Object_rebuildMetamodel.java
index 506c7a7..c68f875 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/mixins/layout/Object_rebuildMetamodel.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/mixins/layout/Object_rebuildMetamodel.java
@@ -22,7 +22,6 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.annotation.Contributed;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.Mixin;
@@ -33,7 +32,15 @@ import org.apache.isis.applib.services.metamodel.MetaModelService;
 
 import lombok.RequiredArgsConstructor;
 
-@Mixin(method="act") 
+@Action(
+        domainEvent = Object_rebuildMetamodel.ActionDomainEvent.class,
+        semantics = SemanticsOf.IDEMPOTENT,
+        restrictTo = RestrictTo.PROTOTYPING
+)
+@ActionLayout(
+        cssClassFa = "fa-sync",
+        position = ActionLayout.Position.PANEL
+)
 @RequiredArgsConstructor
 public class Object_rebuildMetamodel {
 
@@ -42,17 +49,6 @@ public class Object_rebuildMetamodel {
     public static class ActionDomainEvent
     extends org.apache.isis.applib.IsisModuleApplib.ActionDomainEvent<Object_rebuildMetamodel> {}
 
-    @Action(
-            domainEvent = ActionDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT,
-            commandPersistence = CommandPersistence.NOT_PERSISTED,
-            restrictTo = RestrictTo.PROTOTYPING
-            )
-    @ActionLayout(
-            contributed = Contributed.AS_ACTION,
-            cssClassFa = "fa-sync",
-            position = ActionLayout.Position.PANEL
-            )
     @MemberOrder(name = MixinConstants.METADATA_LAYOUT_GROUPNAME, sequence = "800.1")
     public Object act() {
         metaModelService.rebuild(holder.getClass());
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/util/ToString.java b/api/applib/src/main/adoc/modules/applib-classes/examples/util/ToString.java
index 129c52d..6bcd50a 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/util/ToString.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/util/ToString.java
@@ -41,7 +41,7 @@ public class ToString<T> {
         return new ToString<>(name, getter, false);
     }
 
-    public static <T> ToString<T> toStringOmmitIfAbsent(String name, Function<? super T, ?> getter) {
+    public static <T> ToString<T> toStringOmitIfAbsent(String name, Function<? super T, ?> getter) {
         Objects.requireNonNull(name);
         Objects.requireNonNull(getter);
         return new ToString<>(name, getter, true);
@@ -49,10 +49,10 @@ public class ToString<T> {
 
     private final List<String> names = _Lists.newArrayList();
     private final List<Function<? super T, ?>> getters = _Lists.newArrayList();
-    private final BitSet ommitIfAbsent = new BitSet();
+    private final BitSet omitIfAbsent = new BitSet();
 
-    private ToString(String name, Function<? super T, ?> getter, boolean ommitIfAbsent) {
-        addBit(ommitIfAbsent);
+    private ToString(String name, Function<? super T, ?> getter, boolean omitIfAbsent) {
+        addBit(omitIfAbsent);
         names.add(name);
         getters.add(getter);
     }
@@ -66,7 +66,7 @@ public class ToString<T> {
         return this;
     }
 
-    public ToString<T> thenToStringOmmitIfAbsent(String name, Function<? super T, ?> getter){
+    public ToString<T> thenToStringOmitIfAbsent(String name, Function<? super T, ?> getter){
         Objects.requireNonNull(name);
         Objects.requireNonNull(getter);
         addBit(true);
@@ -95,13 +95,12 @@ public class ToString<T> {
 
         return String.format("%s{%s}",
 
-                //ommitIfAbsent.toString(),
                 target.getClass().getSimpleName(),
 
                 getters.stream()
                 .peek(__->index[0]++)
                 .map(getter->getter.apply(target))
-                .filter(value->value!=null || !ommitIfAbsent.get(index[0]))
+                .filter(value->value!=null || !omitIfAbsent.get(index[0]))
                 .map(valueToStringFunction)
                 .map(valueLiteral->names.get(index[0])+"="+valueLiteral)
                 .collect(Collectors.joining(", "))
@@ -114,9 +113,9 @@ public class ToString<T> {
     private void addBit(boolean bit) {
         final int index = names.size();
         if(bit) {
-            ommitIfAbsent.set(index);
+            omitIfAbsent.set(index);
         } else {
-            ommitIfAbsent.clear(index);
+            omitIfAbsent.clear(index);
         }
     }
 
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommandDtoUtils.java b/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommandDtoUtils.java
index b02c91c..635c12c 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommandDtoUtils.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommandDtoUtils.java
@@ -30,7 +30,10 @@ import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
 import javax.xml.bind.Unmarshaller;
 
+import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.util.JaxbUtil;
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.commons.internal.resources._Resources;
 import org.apache.isis.schema.cmd.v2.ActionDto;
 import org.apache.isis.schema.cmd.v2.CommandDto;
@@ -63,6 +66,10 @@ public final class CommandDtoUtils {
         }
     }
 
+    public static CommandDto clone(final CommandDto commandDto) {
+        return fromXml(toXml(commandDto));
+    }
+
     public static CommandDto fromXml(final String xml) {
         return fromXml(new StringReader(xml));
     }
@@ -130,13 +137,21 @@ public final class CommandDtoUtils {
 
     public static void setUserData(
             final CommandDto dto, final String key, final String value) {
-        if(dto == null || key == null) {
+        if(dto == null || key == null || _Strings.isNullOrEmpty(value)) {
             return;
         }
         final MapDto userData = userDataFor(dto);
         CommonDtoUtils.putMapKeyValue(userData, key, value);
     }
 
+    public static void setUserData(
+            final CommandDto dto, final String key, final Bookmark bookmark) {
+        if(dto == null || key == null || bookmark == null) {
+            return;
+        }
+        setUserData(dto, key, bookmark.toString());
+    }
+
     private static MapDto userDataFor(final CommandDto commandDto) {
         MapDto userData = commandDto.getUserData();
         if(userData == null) {
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommonDtoUtils.java b/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommonDtoUtils.java
index 48ce8e3..3fb7f14 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommonDtoUtils.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/util/schema/CommonDtoUtils.java
@@ -33,6 +33,9 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
 
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
+import org.apache.isis.applib.jaxb.JavaTimeXMLGregorianCalendarMarshalling;
+import org.apache.isis.applib.jaxb.JodaTimeXMLGregorianCalendarMarshalling;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.applib.value.Blob;
@@ -220,57 +223,57 @@ public final class CommonDtoUtils {
         }
         case LOCAL_DATE: {
             final LocalDate argValue = (LocalDate) pojo;
-            valueDto.setLocalDate(XmlCalendarFactory.create(argValue));
+            valueDto.setLocalDate(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
             return valueDto;
         }
         case LOCAL_TIME: {
             final LocalTime argValue = (LocalTime) pojo;
-            valueDto.setLocalTime(XmlCalendarFactory.create(argValue));
+            valueDto.setLocalTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
             return valueDto;
         }
         case LOCAL_DATE_TIME: {
             final LocalDateTime argValue = (LocalDateTime) pojo;
-            valueDto.setLocalDateTime(XmlCalendarFactory.create(argValue));
+            valueDto.setLocalDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
             return valueDto;
         }
         case OFFSET_DATE_TIME: {
             final OffsetDateTime argValue = (OffsetDateTime) pojo;
-            valueDto.setOffsetDateTime(XmlCalendarFactory.create(argValue));
+            valueDto.setOffsetDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
             return valueDto;
         }
         case OFFSET_TIME: {
             final OffsetTime argValue = (OffsetTime) pojo;
-            valueDto.setOffsetTime(XmlCalendarFactory.create(argValue));
+            valueDto.setOffsetTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
             return valueDto;
         }
         case ZONED_DATE_TIME: {
             final ZonedDateTime argValue = (ZonedDateTime) pojo;
-            valueDto.setZonedDateTime(XmlCalendarFactory.create(argValue));
+            valueDto.setZonedDateTime(JavaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar2(argValue));
             return valueDto;
         }
         case JODA_DATE_TIME: {
             final org.joda.time.DateTime argValue = (org.joda.time.DateTime) pojo;
-            valueDto.setOffsetDateTime(JodaDateTimeXMLGregorianCalendarAdapter.print(argValue));
+            valueDto.setOffsetDateTime(JodaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case JODA_LOCAL_DATE_TIME: {
             final org.joda.time.LocalDateTime argValue = (org.joda.time.LocalDateTime) pojo;
-            valueDto.setLocalDateTime(JodaLocalDateTimeXMLGregorianCalendarAdapter.print(argValue));
+            valueDto.setLocalDateTime(JodaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case JODA_LOCAL_DATE: {
             final org.joda.time.LocalDate argValue = (org.joda.time.LocalDate) pojo;
-            valueDto.setLocalDate(JodaLocalDateXMLGregorianCalendarAdapter.print(argValue));
+            valueDto.setLocalDate(JodaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case JODA_LOCAL_TIME: {
             final org.joda.time.LocalTime argValue = (org.joda.time.LocalTime) pojo;
-            valueDto.setLocalTime(JodaLocalTimeXMLGregorianCalendarAdapter.print(argValue));
+            valueDto.setLocalTime(JodaTimeXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case JAVA_SQL_TIMESTAMP: {
             final java.sql.Timestamp argValue = (java.sql.Timestamp) pojo;
-            valueDto.setTimestamp(JavaSqlTimestampXmlGregorianCalendarAdapter.print(argValue));
+            valueDto.setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(argValue));
             return valueDto;
         }
         case ENUM: {
@@ -298,7 +301,6 @@ public final class CommonDtoUtils {
             return valueDto;
         }
         case BLOB: {
-
             final Blob blob = (Blob) pojo;
             if(blob != null) {
                 final BlobDto blobDto = new BlobDto();
@@ -374,35 +376,34 @@ public final class CommonDtoUtils {
         case CHAR:
             final String aChar = valueDto.getChar();
             if(_Strings.isNullOrEmpty(aChar)) { return null; }
-            return (Object)aChar.charAt(0);
+            return aChar.charAt(0);
         case BIG_DECIMAL:
             return valueDto.getBigDecimal();
         case BIG_INTEGER:
             return valueDto.getBigInteger();
          // JAVA TIME    
         case LOCAL_DATE:
-            return XmlCalendarFactory.toLocalDate(valueDto.getLocalDate());
+            return JavaTimeXMLGregorianCalendarMarshalling.toLocalDate(valueDto.getLocalDate());
         case LOCAL_TIME:
-            return XmlCalendarFactory.toLocalTime(valueDto.getLocalTime());
+            return JavaTimeXMLGregorianCalendarMarshalling.toLocalTime(valueDto.getLocalTime());
         case LOCAL_DATE_TIME:
-            return XmlCalendarFactory.toLocalDateTime(valueDto.getLocalDateTime());
+            return JavaTimeXMLGregorianCalendarMarshalling.toLocalDateTime(valueDto.getLocalDateTime());
         case OFFSET_DATE_TIME:
-            return XmlCalendarFactory.toOffsetDateTime(valueDto.getOffsetDateTime());
+            return JavaTimeXMLGregorianCalendarMarshalling.toOffsetDateTime(valueDto.getOffsetDateTime());
         case OFFSET_TIME:
-            return XmlCalendarFactory.toOffsetTime(valueDto.getOffsetTime());
+            return JavaTimeXMLGregorianCalendarMarshalling.toOffsetTime(valueDto.getOffsetTime());
         case ZONED_DATE_TIME:
-            return XmlCalendarFactory.toZonedDateTime(valueDto.getZonedDateTime());
+            return JavaTimeXMLGregorianCalendarMarshalling.toZonedDateTime(valueDto.getZonedDateTime());
         case JAVA_SQL_TIMESTAMP:
-            return JavaSqlTimestampXmlGregorianCalendarAdapter.parse(valueDto.getTimestamp());
-        // JODA
+            return JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(valueDto.getTimestamp());
         case JODA_DATE_TIME:
-            return JodaDateTimeXMLGregorianCalendarAdapter.parse(valueDto.getOffsetDateTime());
+            return JodaTimeXMLGregorianCalendarMarshalling.toDateTime(valueDto.getOffsetDateTime());
         case JODA_LOCAL_DATE:
-            return JodaLocalDateXMLGregorianCalendarAdapter.parse(valueDto.getLocalDate());
+            return JodaTimeXMLGregorianCalendarMarshalling.toLocalDate(valueDto.getLocalDate());
         case JODA_LOCAL_DATE_TIME:
-            return JodaLocalDateTimeXMLGregorianCalendarAdapter.parse(valueDto.getLocalDateTime());
+            return JodaTimeXMLGregorianCalendarMarshalling.toLocalDateTime(valueDto.getLocalDateTime());
         case JODA_LOCAL_TIME:
-            return JodaLocalTimeXMLGregorianCalendarAdapter.parse(valueDto.getLocalTime());
+            return JodaTimeXMLGregorianCalendarMarshalling.toLocalTime(valueDto.getLocalTime());
         case ENUM:
             final EnumDto enumDto = valueDto.getEnum();
             final String enumType = enumDto.getEnumType();
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/value/Blob.java b/api/applib/src/main/adoc/modules/applib-classes/examples/value/Blob.java
index ef723ca..3dba41d 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/value/Blob.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/value/Blob.java
@@ -25,13 +25,21 @@ import java.util.Arrays;
 import java.util.Objects;
 
 import javax.activation.MimeType;
+import javax.activation.MimeTypeParseException;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
+import org.apache.isis.applib.annotation.Value;
+import org.apache.isis.applib.jaxb.PrimitiveJaxbAdapters;
 import org.apache.isis.commons.internal.base._Strings;
 
 import lombok.val;
 
 // tag::refguide[]
 // end::refguide[]
+@Value(semanticsProviderName =
+        "org.apache.isis.core.metamodel.facets.value.blobs.BlobValueSemanticsProvider")
+@XmlJavaTypeAdapter(Blob.JaxbToStringAdapter.class)   // for JAXB view model support
 public final class Blob implements NamedWithMimeType {
 
     /**
@@ -149,4 +157,44 @@ public final class Blob implements NamedWithMimeType {
         return getName() + " [" + getMimeType().getBaseType() + "]: " + getBytes().length + " bytes";
     }
 
+    /**
+     * (thread-safe)
+     * @implNote see also BlobValueSemanticsProvider
+     */
+    public static final class JaxbToStringAdapter extends XmlAdapter<String, Blob> {
+
+        private final PrimitiveJaxbAdapters.BytesAdapter bytesAdapter = new PrimitiveJaxbAdapters.BytesAdapter(); // thread-safe
+
+        @Override
+        public Blob unmarshal(String data) throws Exception {
+            if(data==null) {
+                return null;
+            }
+            final int colonIdx = data.indexOf(':');
+            final String name  = data.substring(0, colonIdx);
+            final int colon2Idx  = data.indexOf(":", colonIdx+1);
+            final String mimeTypeBase = data.substring(colonIdx+1, colon2Idx);
+            final String payload = data.substring(colon2Idx+1);
+            final byte[] bytes = bytesAdapter.unmarshal(payload);
+            try {
+                return new Blob(name, new MimeType(mimeTypeBase), bytes);
+            } catch (MimeTypeParseException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public String marshal(Blob blob) throws Exception {
+            if(blob==null) {
+                return null;
+            }
+            String s = blob.getName() +
+                    ':' +
+                    blob.getMimeType().getBaseType() +
+                    ':' +
+                    bytesAdapter.marshal(blob.getBytes());
+            return s;
+        }
+
+    }
 }
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/value/Clob.java b/api/applib/src/main/adoc/modules/applib-classes/examples/value/Clob.java
index d2a6554..0b558de 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/value/Clob.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/value/Clob.java
@@ -21,16 +21,25 @@ package org.apache.isis.applib.value;
 
 import java.io.IOException;
 import java.io.Writer;
+import java.nio.charset.StandardCharsets;
 import java.util.Objects;
 
 import javax.activation.MimeType;
+import javax.activation.MimeTypeParseException;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
+import org.apache.isis.applib.annotation.Value;
+import org.apache.isis.applib.jaxb.PrimitiveJaxbAdapters;
 import org.apache.isis.commons.internal.base._Strings;
 
 import lombok.val;
 
 // tag::refguide[]
 // end::refguide[]
+@Value(semanticsProviderName =
+        "org.apache.isis.core.metamodel.facets.value.clobs.ClobValueSemanticsProvider")
+@XmlJavaTypeAdapter(Clob.JaxbToStringAdapter.class)   // for JAXB view model support
 public final class Clob implements NamedWithMimeType {
 
     private static final long serialVersionUID = 8694189924062378527L;
@@ -142,4 +151,45 @@ public final class Clob implements NamedWithMimeType {
         return getName() + " [" + getMimeType().getBaseType() + "]: " + getChars().length() + " chars";
     }
 
+    /**
+     * (thread-safe)
+     * @implNote see also ClobValueSemanticsProvider
+     */
+    public static final class JaxbToStringAdapter extends XmlAdapter<String, Clob> {
+
+        private final PrimitiveJaxbAdapters.BytesAdapter bytesAdapter = new PrimitiveJaxbAdapters.BytesAdapter(); // thread-safe
+
+        @Override
+        public Clob unmarshal(String data) throws Exception {
+            if(data==null) {
+                return null;
+            }
+            final int colonIdx = data.indexOf(':');
+            final String name  = data.substring(0, colonIdx);
+            final int colon2Idx  = data.indexOf(":", colonIdx+1);
+            final String mimeTypeBase = data.substring(colonIdx+1, colon2Idx);
+            final String payload = data.substring(colon2Idx+1);
+            final byte[] bytes = bytesAdapter.unmarshal(payload);
+            try {
+                return new Clob(name, new MimeType(mimeTypeBase), new String(bytes, StandardCharsets.UTF_8));
+            } catch (MimeTypeParseException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public String marshal(Clob clob) throws Exception {
+            if(clob==null) {
+                return null;
+            }
+            return new StringBuilder()
+            .append(clob.getName())
+            .append(':')
+            .append(clob.getMimeType().getBaseType())
+            .append(':')
+            .append(bytesAdapter.marshal(clob.getChars().toString().getBytes(StandardCharsets.UTF_8)))
+            .toString();
+        }
+
+    }
 }
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/value/HasHtml.java b/api/applib/src/main/adoc/modules/applib-classes/examples/value/HasHtml.java
index 2905098..ecb6fec 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/value/HasHtml.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/value/HasHtml.java
@@ -19,11 +19,6 @@
 
 package org.apache.isis.applib.value;
 
-import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
-
-import org.apache.isis.applib.annotation.Value;
-import org.apache.isis.applib.util.JaxbAdapters;
-
 // tag::refguide[]
 // end::refguide[]
 public interface HasHtml {
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/value/LocalResourcePath.java b/api/applib/src/main/adoc/modules/applib-classes/examples/value/LocalResourcePath.java
index a0eb7c6..47523bc 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/value/LocalResourcePath.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/value/LocalResourcePath.java
@@ -23,6 +23,8 @@ import java.io.Serializable;
 import java.net.URISyntaxException;
 
 import javax.annotation.Nonnull;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
 import org.apache.isis.applib.annotation.Value;
 
@@ -33,8 +35,11 @@ import lombok.NonNull;
  */
 // tag::refguide[]
 // end::refguide[]
-@Value(semanticsProviderName = "org.apache.isis.core.metamodel.facets.value.localrespath.LocalResourcePathValueSemanticsProvider")
+@Value(semanticsProviderName =
+        "org.apache.isis.core.metamodel.facets.value.localrespath.LocalResourcePathValueSemanticsProvider")
+@XmlJavaTypeAdapter(LocalResourcePath.JaxbToStringAdapter.class)   // for JAXB view model support
 public final class LocalResourcePath implements Serializable {
+
     private static final long serialVersionUID = 1L;
     @NonNull private final String path;
 
@@ -65,7 +70,7 @@ public final class LocalResourcePath implements Serializable {
         if(obj==null) {
             return false;
         }
-        return (obj instanceof LocalResourcePath) ?	isEqualTo((LocalResourcePath)obj) : false;
+        return (obj instanceof LocalResourcePath) && isEqualTo((LocalResourcePath) obj);
     }
     
     @Override
@@ -90,9 +95,20 @@ public final class LocalResourcePath implements Serializable {
             // used for syntax testing
             new java.net.URI("http://localhost/"+path);
         } catch (URISyntaxException e) {
-            throw new IllegalArgumentException("the given local path has an invalid syntax: '"+path+"'", e);
+            throw new IllegalArgumentException(String.format("the given local path has an invalid syntax: '%s'", path), e);
         }
-
     }
 
+
+    public static class JaxbToStringAdapter extends XmlAdapter<String, LocalResourcePath> {
+        @Override
+        public LocalResourcePath unmarshal(String path) {
+            return path != null ? new LocalResourcePath(path) : null;
+        }
+
+        @Override
+        public String marshal(LocalResourcePath localResourcePath) {
+            return localResourcePath != null ? localResourcePath.getPath() : null;
+        }
+    }
 }
\ No newline at end of file
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/value/Markup.java b/api/applib/src/main/adoc/modules/applib-classes/examples/value/Markup.java
index 774b148..478d3b4 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/value/Markup.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/value/Markup.java
@@ -19,10 +19,15 @@
 
 package org.apache.isis.applib.value;
 
+import java.io.Serializable;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
 import org.apache.isis.applib.annotation.Value;
-import org.apache.isis.applib.util.JaxbAdapters;
+import org.apache.isis.core.commons.internal.base._Strings;
 
 /**
  * Immutable value type holding pre-rendered HTML.
@@ -30,11 +35,12 @@ import org.apache.isis.applib.util.JaxbAdapters;
  */
 // tag::refguide[]
 // end::refguide[]
-@Value(semanticsProviderName =
+@Value(semanticsProviderName = 
         "org.apache.isis.core.metamodel.facets.value.markup.MarkupValueSemanticsProvider")
-@XmlJavaTypeAdapter(JaxbAdapters.MarkupAdapter.class)   // for JAXB view model support
-public class Markup implements HasHtml {   // TODO: should be final
+@XmlJavaTypeAdapter(Markup.JaxbToStringAdapter.class)   // for JAXB view model support
+public class Markup implements HasHtml, Serializable {   // TODO: should be final
 
+    private static final long serialVersionUID = 1L;
     private final String html;
 
     public Markup() {
@@ -78,4 +84,30 @@ public class Markup implements HasHtml {   // TODO: should be final
         return "Markup[length="+html.length()+", html="+html+"]";
     }
 
+    public static final class JaxbToStringAdapter extends XmlAdapter<String, Markup> {
+
+        /**
+         * Is threadsafe, see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Encoder.html">JDK8 javadocs</a>
+         */
+        private final Base64.Encoder encoder = Base64.getEncoder();
+        /**
+         * Is threadsafe, see <a href="https://docs.oracle.com/javase/8/docs/api/java/util/Base64.Decoder.html">JDK8 javadocs</a>
+         */
+        private final Base64.Decoder decoder = Base64.getDecoder(); // is thread-safe ?
+
+        @Override
+        public Markup unmarshal(String v) throws Exception {
+            return v != null
+                    ? new Markup(_Strings.ofBytes(decoder.decode(v), StandardCharsets.UTF_8))
+                    : null;
+        }
+
+        @Override
+        public String marshal(Markup v) throws Exception {
+            return v != null
+                    ? encoder.encodeToString(_Strings.toBytes(v.asHtml(), StandardCharsets.UTF_8))
+                    : null;
+        }
+    }
+
 }
diff --git a/api/applib/src/main/adoc/modules/applib-classes/examples/value/Password.java b/api/applib/src/main/adoc/modules/applib-classes/examples/value/Password.java
index 81ff445..45adcf7 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/examples/value/Password.java
+++ b/api/applib/src/main/adoc/modules/applib-classes/examples/value/Password.java
@@ -24,6 +24,7 @@ import java.util.Objects;
 
 import javax.xml.bind.annotation.XmlAccessType;
 import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 
 import org.apache.isis.applib.annotation.Value;
 
@@ -31,6 +32,7 @@ import org.apache.isis.applib.annotation.Value;
 // end::refguide[]
 @Value(semanticsProviderName = "org.apache.isis.core.metamodel.facets.value.password.PasswordValueSemanticsProvider")
 @XmlAccessorType(XmlAccessType.FIELD)
+// @XmlJavaTypeAdapter(Password.JaxbToStringAdapter.class) // TODO: not automatically registered because not secure enough.  Instead we should set up some sort of mechanism to encrypt.
 @lombok.Value
 public class Password implements Serializable {
     
@@ -63,8 +65,19 @@ public class Password implements Serializable {
     }
 
 
+    public static class JaxbToStringAdapter extends javax.xml.bind.annotation.adapters.XmlAdapter<String, Password> {
+        @Override
+        public Password unmarshal(String str) throws Exception {
+            return str != null
+                    ? new Password(str)
+                    : null;
+        }
 
-    
-
-    
+        @Override
+        public String marshal(Password password) throws Exception {
+            return password != null
+                    ? password.getPassword()
+                    : null;
+        }
+    }
 }
diff --git a/api/applib/src/main/adoc/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc b/api/applib/src/main/adoc/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc
index ceb933a..c636f2d 100644
--- a/api/applib/src/main/adoc/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc
+++ b/api/applib/src/main/adoc/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc
@@ -9,20 +9,16 @@ The interfaces listed in this chapter act as contributees; they allow domain ser
 [[HasUniqueId]]
 == `HasUniqueId`
 
-The `HasUniqueId` interface is a mix-in for any domain objects that reference a transaction id, such as auditing entries or commands, or for xref:refguide:applib-svc:InteractionContext.adoc[`Interaction`]s persisted as published events.
-
-[NOTE]
-====
-This identifier actually is for the request/interaction in which the object was created, so is actually now mis-named.
-====
+The `HasUniqueId` interface is a mix-in for any domain objects that are uniquely identified, in particular to represent a system-level request or interaction.
+The canonical usage is where the unique identifier is actually a transaction id, as implemented by auditing entries or commands, or for xref:refguide:applib-svc:InteractionContext.adoc[`Interaction`]s persisted as published events.
 
 The interface is defined is:
 
 [source,java]
 ----
 public interface HasUniqueId {
-    public UUID getTransactionId();                             // <.>
-    public void setTransactionId(final UUID transactionId);
+    public UUID getUniqueId();                             // <.>
+    public void setUniqueId(final UUID uniqueId);
 }
 ----
 <.> unique identifier (a GUID) of this request/interaction.
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/DomainChangeRecord.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/DomainChangeRecord.java
new file mode 100644
index 0000000..7cfc48d
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/DomainChangeRecord.java
@@ -0,0 +1,155 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.applib.services;
+
+import java.sql.Timestamp;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.message.MessageService;
+import org.apache.isis.applib.services.metamodel.BeanSort;
+import org.apache.isis.applib.services.metamodel.MetaModelService;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
+
+
+/**
+ * An abstraction of some sort of recorded change to a domain object: commands, audit entries or published events.
+ */
+public interface DomainChangeRecord extends HasUniqueId, HasUsername {
+
+    enum ChangeType {
+        COMMAND,
+        AUDIT_ENTRY,
+        PUBLISHED_INTERACTION;
+        @Override
+        public String toString() {
+            return name().replace("_", " ");
+        }
+    }
+
+    /**
+     * Distinguishes commands from audit entries from published events/interactions (when these are shown mixed together in a (standalone) table).
+     */
+    @Property
+    @PropertyLayout(hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
+    @MemberOrder(name="Identifiers", sequence = "1")
+    ChangeType getType();
+
+
+    /**
+     * The unique identifier (a GUID) of the transaction in which this change occurred.
+     */
+    @Property
+    @MemberOrder(name="Identifiers",sequence = "50")
+    UUID getUniqueId();
+
+
+    /**
+     * The user that caused the change.
+     */
+    @Property
+    @MemberOrder(name="Identifiers", sequence = "10")
+    String getUsername();
+
+
+    /**
+     * The time that the change occurred.
+     */
+    @Property
+    @MemberOrder(name="Identifiers", sequence = "20")
+    Timestamp getTimestamp();
+
+
+    /**
+     * The object type of the domain object being changed.
+     */
+    @Property
+    @PropertyLayout(named="Object Type")
+    @MemberOrder(name="Target", sequence = "10")
+    default String getTargetObjectType() {
+        return getTarget().getObjectType();
+    }
+
+
+
+    /**
+     * The {@link Bookmark} identifying the domain object that has changed.
+     */
+    @Property
+    @PropertyLayout(named="Object")
+    @MemberOrder(name="Target", sequence="30")
+    Bookmark getTarget();
+
+
+    /**
+     * The member interaction (ie action invocation or property edit) which caused the domain object to be changed.
+     *
+     * <p>
+     *     Populated for commands and for published events that represent action invocations or property edits.
+     * </p>
+     */
+    @Property(optionality = Optionality.OPTIONAL)
+    @PropertyLayout(named="Member", hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
+    @MemberOrder(name="Target", sequence = "20")
+    String getTargetMember();
+
+
+    /**
+     * The value of the property prior to it being changed.
+     *
+     * <p>
+     * Populated only for audit entries.
+     * </p>
+     */
+    @Property(optionality = Optionality.OPTIONAL)
+    @PropertyLayout(hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
+    @MemberOrder(name="Detail",sequence = "6")
+    String getPreValue();
+
+
+    /**
+     * The value of the property after it has changed.
+     *
+     * <p>
+     * Populated only for audit entries.
+     * </p>
+     */
+    @Property(optionality = Optionality.MANDATORY)
+    @PropertyLayout(hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
+    @MemberOrder(name="Detail",sequence = "7")
+    String getPostValue();
+
+
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/DomainChangeRecord_openTargetObject.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/DomainChangeRecord_openTargetObject.java
new file mode 100644
index 0000000..7033bb5
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/DomainChangeRecord_openTargetObject.java
@@ -0,0 +1,61 @@
+package org.apache.isis.applib.services;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.message.MessageService;
+import org.apache.isis.applib.services.metamodel.BeanSort;
+import org.apache.isis.applib.services.metamodel.MetaModelService;
+
+@Action(
+        semantics = SemanticsOf.SAFE
+        , associateWith = "target"
+        , associateWithSequence = "1"
+)
+@ActionLayout(named = "Open")
+public class DomainChangeRecord_openTargetObject {
+
+    private final DomainChangeRecord domainChangeRecord;
+    public DomainChangeRecord_openTargetObject(DomainChangeRecord domainChangeRecord) {
+        this.domainChangeRecord = domainChangeRecord;
+    }
+
+    @Action(semantics = SemanticsOf.SAFE, associateWith = "target", associateWithSequence = "1")
+    @ActionLayout(named = "Open")
+    public Object openTargetObject() {
+        try {
+            return bookmarkService != null
+                    ? bookmarkService.lookup(domainChangeRecord.getTarget())
+                    : null;
+        } catch(RuntimeException ex) {
+            if(ex.getClass().getName().contains("ObjectNotFoundException")) {
+                messageService.warnUser("Object not found - has it since been deleted?");
+                return null;
+            }
+            throw ex;
+        }
+    }
+
+    public boolean hideOpenTargetObject() {
+        return domainChangeRecord.getTarget() == null;
+    }
+
+    public String disableOpenTargetObject() {
+        final Object targetObject = domainChangeRecord.getTarget();
+        if (targetObject == null) {
+            return null;
+        }
+        final BeanSort sortOfObject = metaModelService.sortOf(domainChangeRecord.getTarget(), MetaModelService.Mode.RELAXED);
+        return !(sortOfObject.isViewModel() || sortOfObject.isEntity())
+                ? "Can only open view models or entities"
+                : null;
+    }
+
+    @Inject BookmarkService bookmarkService;
+    @Inject MessageService messageService;
+    @Inject MetaModelService metaModelService;
+
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/HasUsername.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/HasUsername.java
index 7f7a240..4860b92 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/HasUsername.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/HasUsername.java
@@ -29,6 +29,10 @@ package org.apache.isis.applib.services;
 // tag::refguide[]
 public interface HasUsername {
 
+    /**
+     * The user that created this object.
+     * @return
+     */
     String getUsername();
 
 }
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/TransactionScopeListener.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/TransactionScopeListener.java
index 4c01d24..028bf40 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/TransactionScopeListener.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/TransactionScopeListener.java
@@ -19,11 +19,14 @@
 
 package org.apache.isis.applib.services;
 
+import org.apache.isis.applib.annotation.IsisInteractionScope;
+
 /**
- * Domain services that need to be aware of transaction boundaries can implement this interface.
+ * Domain services that need to be aware of transaction boundaries can
+ * implement this interface.
  * 
- * @apiNote Implementing services most likely need to be scoped in a way that binds the scope to 
- * the current thread (eg. {@link IsisInteractionScope}) 
+ * @apiNote Implementing services most likely need to be scoped in a way that
+ * binds the scope to the current thread (eg. {@link IsisInteractionScope})
  *  
  * @since 2.0 (renamed from WithTransactionScope)
  */
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/background/BackgroundCommandService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/background/BackgroundCommandService.java
deleted file mode 100644
index 4fcf317..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/background/BackgroundCommandService.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.background;
-
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-
-/**
- * Persists a {@link org.apache.isis.schema.cmd.v1.CommandDto command-reified} action such that it can be executed asynchronously,
- * for example through a Quartz scheduler.
- *
- * <p>
- * Separate from {@link BackgroundService} primarily so that the default
- * implementation, <tt>BackgroundServiceDefault</tt> (in <tt>isis-module-background</tt>) can
- * delegate to different implementations of this service.
- *
- * <p>
- * There is currently only implementation of this service, <tt>BackgroundCommandServiceJdo</tt> in
- * <tt>o.a.i.module:isis-module-command-jdo</tt>.  That implementation has no UI and no side-effects (the programmatic
- * API is through {@link org.apache.isis.applib.services.background.BackgroundService}).  It is therefore
- * annotated with {@link org.apache.isis.applib.annotation.DomainService} so that it is automatically registered as
- * a service.
- *
- */
-// tag::refguide[]
-public interface BackgroundCommandService extends AutoCloseable {
-
-    void schedule(
-            final CommandDto dto,
-            final Command parentCommand,
-            final String targetClassName,
-            final String targetActionName,
-            final String targetArgs);
-
-    /**
-     * @apiNote refined from AutoCloseable to not throw catched exceptions
-     */
-    @Override
-    default void close() {
-    }
-
-}
-// end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/bookmark/Bookmark.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/bookmark/Bookmark.java
index 524fc48..0c4e17b 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/bookmark/Bookmark.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/bookmark/Bookmark.java
@@ -73,6 +73,7 @@ public class Bookmark implements Serializable {
 
     /**
      * Round-trip with {@link #toString()} representation.
+     * @return
      */
     // tag::refguide[]
     public static Optional<Bookmark> parse(@Nullable String str) {
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/clock/ClockService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/clock/ClockService.java
index 597a56b..4583a4c 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/clock/ClockService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/clock/ClockService.java
@@ -18,15 +18,11 @@
  */
 package org.apache.isis.applib.services.clock;
 
-import java.sql.Timestamp;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.OffsetDateTime;
 import java.util.TimeZone;
 
 import javax.inject.Named;
+import javax.xml.datatype.XMLGregorianCalendar;
 
-import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Primary;
@@ -35,6 +31,7 @@ import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.clock.Clock;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
 
 /**
  * This service allows an application to be decoupled from the system time.  The most common use case is in support of
@@ -66,6 +63,10 @@ public class ClockService {
         return Clock.getTimeAsJavaSqlTimestamp();
     }
 
+    public XMLGregorianCalendar nowAsXMLGregorianCalendar() {
+        return JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(nowAsJavaSqlTimestamp());
+    }
+
     public long nowAsMillis() {
         return Clock.getEpochMillis();
     }
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/Command.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/Command.java
index f030cb6..dc55a40 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/Command.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/Command.java
@@ -19,217 +19,175 @@
 package org.apache.isis.applib.services.command;
 
 import java.sql.Timestamp;
+import java.util.UUID;
 
-import org.apache.isis.applib.Identifier;
-import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.events.domain.ActionDomainEvent;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
 import org.apache.isis.applib.services.HasUniqueId;
-import org.apache.isis.applib.services.background.BackgroundCommandService;
+import org.apache.isis.applib.services.HasUsername;
 import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.commanddto.HasCommandDto;
 import org.apache.isis.applib.services.iactn.Interaction;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.applib.services.wrapper.control.AsyncControl;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
+import lombok.Getter;
+
 /**
  * Represents the <i>intention to</i> invoke either an action or modify a property.  There can be only one such
- * intention per (web) request, so a command is in effect request-scoped.  Note that {@link CommandContext} domain
- * service - from which the current {@link Command} can be obtained - is indeed annotated with
- * {@link javax.enterprise.context.RequestScoped @RequestScoped}.
+ * intention per (web) request, so a command is in effect interaction-scoped.
  *
  * <p>
- * Each Command can be reified into a {@link Command#getMemento() memento} by way of the (internal)
- * <tt>CommandDtoServiceInternal</tt> domain service; typically corresponding to the XML equivalent of a
- * {@link CommandDto} and conforming to the Apache Isis <a href="http://isis.apache.org/schema/cmd/">cmd</a> schema.
+ * Each Command holds a {@link CommandDto} (see Apache Isis <a href="http://isis.apache.org/schema/cmd/">cmd</a> schema)
+ * which reifies all the details in a serializable form.
  * </p>
  *
  * <p>
- *     The {@link Command} interface also captures details of the corresponding action invocation (or property edit),
+ *     It also captures details of the corresponding action invocation (or property edit),
  *     specifically when that action/edit {@link Command#getStartedAt() started} or
  *     {@link Command#getCompletedAt() completed}, and its result, either a {@link Command#getResult() return value}
  *     or an {@link Command#getException() exception}.  Also captures a stack of {@link ActionDomainEvent}s.
  * </p>
  *
  * <p>
- *     Note that when invoking an action, other actions may be invoked courtesy of the {@link WrapperFactory}.  These
- *     "sub-actions" do <i>not</i> modify the contents of the command object; in other words think of the command
+ *     Note that when invoking an action, other actions may be invoked courtesy
+ *     of the {@link WrapperFactory}.  These "sub-actions" do <i>not</i> modify
+ *     the contents of the current command object; in other words think of the command
  *     object as representing the outer-most originating action.
  * </p>
  *
  * <p>
- *     <b>NOTE</b>: in Isis v1.x, one of the responsibilities of {@link Command} was to generate unique sequence numbers for
- *     a given transactionId, where there were three possible sequences that might be generated.
- *     <ul>
- *         <li>
- *         <p>the sequence of changed domain objects being published by the
- *         {@link org.apache.isis.applib.services.publish.PublisherService#publish(Interaction.Execution)}
- *         </p>
- *         <p>
- *         In v2 ... TODO[2158] - what replaces this?
- *         </p>
- *         </li>
- *
- *         <li>
- *         <p>
- *         The sequence of wrapped action invocations (each being published)
- *         </p>
- *         <p>
- *         In v2 this is now done by {@link Interaction#next(String) Interaction} itself.
- *         </p>
- *         </li>
- *
- *         <li>
- *         <p>
- *         and finally, one or more background commands that might be scheduled via the <code>BackgroundService</code>.
- *         </p>
- *         <p>
- *         In v2 ... TODO[2158] - what replaces this?
- *         </p>
- *         </li>
- *     </ul>
- *
+ *     That said, if the sub-action is invoked asynchronously (using
+ *     {@link WrapperFactory#asyncWrap(Object, AsyncControl)} or
+ *     {@link WrapperFactory#asyncWrapMixin(Class, Object, AsyncControl)}), then
+ *     a separate {@link Command} object
+ *     is created, and the originating {@link Command} is set to be its
+ *     {@link Command#getParent() parent}.
  * </p>
- *
  */
 // tag::refguide[]
-public interface Command extends HasUniqueId {
+public class Command implements HasUniqueId, HasUsername, HasCommandDto {
 
     // end::refguide[]
     /**
-     * The user that created the command.
-     */
-    // tag::refguide[]
-    String getUser();                           // <.>
-    // end::refguide[]
-
-    /**
-     * The date/time at which this command was created.
-     */
-    // tag::refguide[]
-    Timestamp getTimestamp();                   // <.>
-    // end::refguide[]
-
-    /**
-     * {@link Bookmark} of the target object (entity or service) on which this action was performed.
+     * Unique identifier for the command.
      *
      * <p>
-     * Will only be populated if a {@link BookmarkService} has been configured.
+     *     Derived from {@link #getCommandDto()}'s {@link CommandDto#getTransactionId()}
      * </p>
      */
+    @Override
     // tag::refguide[]
-    Bookmark getTarget();                       // <.>
-    // end::refguide[]
-
-    /**
-     * Holds a string representation of the invoked action, or the edited property, equivalent to
-     * {@link Identifier#toClassAndNameIdentityString()}.
-     */
-    // tag::refguide[]
-    String getMemberIdentifier();               // <.>
-    // end::refguide[]
-
+    public UUID getUniqueId() {                 // <.>
+        // ...
+        // end::refguide[]
+        return commandDto != null
+                ? UUID.fromString(commandDto.getTransactionId())
+                : null;
+    }
     /**
-     * A human-friendly description of the class of the target object.
+     * The user that created the command.
+     *
+     * <p>
+     *     Derived from {@link #getCommandDto()}'s {@link CommandDto#getUser()}
+     * </p>
      */
+    @Override
     // tag::refguide[]
-    String getTargetClass();                    // <.>
-    // end::refguide[]
+    public String getUsername() {               // <.>
+        // ...
+        // end::refguide[]
+        return commandDto != null
+                ? commandDto.getUser()
+                : null;
+    }
 
     /**
-     * The human-friendly name of the action invoked/property edited on the target object.
+     * The date/time at which this command was created.
+     *
+     * <p>
+     *     Derived from {@link #getCommandDto()}'s {@link CommandDto#getTimestamp()}.
+     * </p>
      */
     // tag::refguide[]
-    String getTargetAction();                   // <.>
-    // end::refguide[]
+    public Timestamp getTimestamp() {           // <.>
+        // ...
+        // end::refguide[]
+        return commandDto != null
+                ? JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimestamp())
+                : null;
+    }
 
     /**
-     * A human-friendly description of the arguments with which the action was invoked.
+     * Serializable representation of the action invocation/property edit.
+     *
+     * <p>
+     *     When the framework sets this (through an internal API), it is
+     *     expected to have {@link CommandDto#getTransactionId()},
+     *     {@link CommandDto#getUser()}, {@link CommandDto#getTimestamp()},
+     *     {@link CommandDto#getTargets()} and {@link CommandDto#getMember()}
+     *     to be populated.  The {@link #getUniqueId()}, {@link #getUsername()},
+     *     {@link #getTimestamp()} and {@link #getTarget()} are all derived
+     *     from the provided {@link CommandDto}.
+     * </p>
      */
     // tag::refguide[]
-    String getArguments();                      // <.>
+    @Getter
+    private CommandDto commandDto;              // <.>
     // end::refguide[]
 
     /**
-     *
-     * A formal (XML or similar) specification of the action to invoke/being invoked.
+     * Derived from {@link #getCommandDto()}, is the {@link Bookmark} of
+     * the target object (entity or service) on which this action/edit was performed.
      */
     // tag::refguide[]
-    String getMemento();                        // <.>
+    public Bookmark getTarget() {               // <.>
+        return commandDto != null
+                ? Bookmark.from(commandDto.getTargets().getOid().get(0))
+                : null;
+    }
     // end::refguide[]
 
     /**
-     *
-     * The mechanism by which this command is to be executed, either synchronously &quot;in the
-     * {@link CommandExecuteIn#FOREGROUND foreground}&quot; or is to be executed asynchronously &quot;in the
-     * {@link CommandExecuteIn#BACKGROUND background}&quot; through the {@link BackgroundCommandService}.
+     * Derived from {@link #getCommandDto()}, holds a string
+     * representation of the invoked action, or the edited property.
      */
     // tag::refguide[]
-    CommandExecuteIn getExecuteIn();            // <.>
-    // end::refguide[]
-
-    // tag::refguide2[]
-    enum Executor {
-        // end::refguide2[]
-        /**
-         * Command being executed by the end-user.
-         */
-        // tag::refguide2[]
-        USER,
-        // end::refguide2[]
-        /**
-         * Command being executed by a background execution service.
-         */
-        // tag::refguide2[]
-        BACKGROUND,
-        // end::refguide2[]
-        /**
-         * Command being executed for some other reason, eg as result of redirect-after-post, or the homePage action.
-         */
-        // tag::refguide2[]
-        OTHER
+    public String getLogicalMemberIdentifier() {    // <.>
+        return commandDto != null
+                    ? commandDto.getMember().getLogicalMemberIdentifier()
+                    : null;
     }
-    // end::refguide2[]
+    // end::refguide[]
 
     /**
-     * The (current) executor of this command.
+     * For async commands created through the {@link WrapperFactory},
+     * captures the parent command.
      *
      * <p>
-     * Note that (even for implementations of {@link BackgroundCommandService} that persist {@link Command}s), this
-     * property is never (likely to be) persisted, because it is always updated to indicate how the command is
-     * currently being executed.
+     *     Will return <code>null</code> if there is no parent.
+     * </p>
      *
-     * <p>
-     * If the {@link #getExecutor() executor} matches the required {@link #getExecuteIn() execution policy}, then the
-     * command actually is executed.  The combinations are:
-     * <ul>
-     * <li>executor = USER, executeIn = FOREGROUND, then execute</li>
-     * <li>executor = USER, executeIn = BACKGROUND, then persist and return persisted command as a placeholder for the result</li>
-     * <li>executor = BACKGROUND, executeIn = FOREGROUND, then ignore</li>
-     * <li>executor = BACKGROUND, executeIn = BACKGROUND, then execute, update the command with result</li>
-     * </ul>
+     * @see WrapperFactory#asyncWrap(Object, AsyncControl)
+     * @see WrapperFactory#asyncWrapMixin(Class, Object, AsyncControl)
      *
      */
     // tag::refguide[]
-    Executor getExecutor();                     // <.>
+    @Getter
+    private Command parent;                     // <.>
     // end::refguide[]
 
     /**
-     * For an command that has actually been executed, holds the date/time at which the {@link Interaction} that
-     * executed the command started.
+     * For an command that has actually been executed, holds the date/time at
+     * which the {@link Interaction} that executed the command started.
      *
-     * <p>
-     *     Previously this field was deprecated (on the basis that the startedAt is also held in
-     *     {@link Interaction.Execution#getStartedAt()}). However, this property is now used in master/slave
-     *     replay scenarios which may query a persisted Command.
-     * </p>
-     *
-     * See also {@link Interaction#getCurrentExecution()} and
-     * {@link Interaction.Execution#getStartedAt()}.
+     * @see Interaction#getCurrentExecution()
+     * @see Interaction.Execution#getStartedAt()
      */
     // tag::refguide[]
-    Timestamp getStartedAt();                   // <.>
+    @Getter
+    private Timestamp startedAt;                // <.>
     // end::refguide[]
 
     /**
@@ -246,224 +204,144 @@ public interface Command extends HasUniqueId {
      * {@link Interaction.Execution#getCompletedAt()}.
      */
     // tag::refguide[]
-    Timestamp getCompletedAt();                 // <.>
+    @Getter
+    private Timestamp completedAt;              // <.>
     // end::refguide[]
 
     /**
-     * For actions created through the {@link BackgroundService} and {@link BackgroundCommandService},
-     * captures the parent action.
-     */
-    // tag::refguide[]
-    Command getParent();                        // <.>
-    // end::refguide[]
-
-    /**
-     * For an command that has actually been executed, holds the exception stack
-     * trace if the action invocation/property modification threw an exception.
+     * For a command that has actually been executed, holds a {@link Bookmark}
+     * to the object returned by the corresponding action/property modification.
      *
      * <p>
-     *     Previously this field was deprecated (on the basis that the exception is also held in
-     *     {@link Interaction.Execution#getThrew()}). However, this property is now used in master/slave
-     *     replay scenarios which may query a persisted Command.
+     *     This property is used in replay scenarios to verify the outcome of
+     *     the replayed command, eg for regression testing.
      * </p>
      *
-     * See also {@link Interaction#getCurrentExecution()} and  {@link org.apache.isis.applib.services.iactn.Interaction.Execution#getThrew()}.
+     * See also  {@link Interaction#getCurrentExecution()} and
+     * {@link org.apache.isis.applib.services.iactn.Interaction.Execution#getReturned()}.
      */
     // tag::refguide[]
-    String getException();                      // <.>
+    @Getter
+    private Bookmark result;                    // <.>
     // end::refguide[]
 
     /**
-     * For an command that has actually been executed, holds a {@link Bookmark} to the object returned by the corresponding action/property modification.
+     * For a command that has actually been executed, holds the exception stack
+     * trace if the action invocation/property modification threw an exception.
      *
      * <p>
-     *     Previously this field was deprecated (on the basis that the returned value is also held in
-     *     {@link Interaction.Execution#getReturned()}). However, this property is now used in master/slave
-     *     replay scenarios which may query a persisted Command.
+     *     This property is used in replay scenarios to verify the outcome of
+     *     the replayed command, eg for regression testing.
      * </p>
      *
-     * See also  {@link Interaction#getCurrentExecution()} and  {@link org.apache.isis.applib.services.iactn.Interaction.Execution#getReturned()}.
+     * See also {@link Interaction#getCurrentExecution()} and
+     * {@link org.apache.isis.applib.services.iactn.Interaction.Execution#getThrew()}.
      */
     // tag::refguide[]
-    Bookmark getResult();                       // <.>
+    @Getter
+    private Throwable exception;                    // <.>
     // end::refguide[]
 
     /**
-     * Whether this command should ultimately be persisted (if the configured {@link BackgroundCommandService} supports
-     * it) or not.
+     * Whether this command resulted in a change of state to the system.
      *
      * <p>
-     * If the action to be executed has been annotated with the {@link Action#command()} attribute
-     * then (unless its {@link Action#commandPersistence()} persistence} attribute has been set to a different value
-     * than its default of {@link org.apache.isis.applib.annotation.CommandPersistence#PERSISTED persisted}), the
-     * {@link Command} object will be persisted.
-     *
-     * <p>
-     * However, it is possible to prevent the {@link Command} object from ever being persisted by setting the
-     * {@link org.apache.isis.applib.annotation.Action#commandPersistence() persistence} attribute to
-     * {@link org.apache.isis.applib.annotation.CommandPersistence#NOT_PERSISTED}, or it can be set to
-     * {@link org.apache.isis.applib.annotation.CommandPersistence#IF_HINTED}, meaning it is dependent
-     * on whether {@link #setPersistHint(boolean) a hint has been set} by some other means.
+     *     This can be used as a hint to decide whether to persist the command
+     *     to a datastore, for example for auditing (though
+     *     {@link org.apache.isis.applib.services.publish.PublisherService} is
+     *     an alternative for that use case) or so that it can be retrieved
+     *     and replayed on another system, eg for regression testing.
+     * </p>
      *
-     * <p>
-     * For example, a {@link BackgroundCommandService} implementation that creates persisted background commands ought
-     * associate them (via its {@link Command#getParent() parent}) to an original persisted
-     * {@link Command}.  The hinting mechanism allows the service to suggest that the parent command be persisted so
-     * that the app can then provide a mechanism to find all child background commands for that original parent command.
      */
     // tag::refguide[]
-    CommandPersistence getPersistence();        // <.>
+    @Getter
+    private boolean systemStateChanged;                // <.>
     // end::refguide[]
 
-    /**
-     * Whether that this {@link Command} should be persisted, if possible.
-     */
-    // tag::refguide[]
-    boolean isPersistHint();                    // <.>
-    // end::refguide[]
 
-    /**
-     * <b>NOT API</b>: intended to be called only by the framework.
-     */
-    public static interface Internal {
+    private final Updater UPDATER = new Updater();
 
+    public class Updater implements CommandOutcomeHandler {
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          *
          * <p>
-         * Implementation notes: set when the Isis PersistenceSession is opened.
-         */
-        void setUser(String user);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the Isis PersistenceSession is opened.
-         */
-        void setTimestamp(Timestamp timestamp);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the action is invoked (in the ActionInvocationFacet).
-         */
-        void setTarget(Bookmark target);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the action is invoked (in <tt>ActionInvocationFacet</tt>) or in
-         * property edited (in <tt>PropertySetterFacet</tt>).
-         */
-        void setMemberIdentifier(String memberIdentifier);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>) or property edited
-         * (in the <tt>PropertySetterOrClearFacet</tt>).
+         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>).
+         * @param commandDto
          */
-        void setTargetAction(String targetAction);
-
-
+        public void setCommandDto(final CommandDto commandDto, final int targetIdx) {
+            Command.this.commandDto = commandDto;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          *
          * <p>
-         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>).
+         *     Only populated for async commands created through the
+         *     {@link WrapperFactory}.
+         * </p>
          */
-        void setArguments(final String arguments);
-
+        public void setParent(Command parent) {
+            Command.this.parent = parent;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setExecutor(final Executor executor);
-
+        @Override
+        public Timestamp getStartedAt() {
+            return Command.this.getStartedAt();
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setResult(Bookmark resultBookmark);
-
+        @Override
+        public void setStartedAt(Timestamp startedAt) {
+            Command.this.startedAt = startedAt;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setException(String stackTrace);
-
+        @Override
+        public void setCompletedAt(final Timestamp completed) {
+            Command.this.completedAt = completed;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setParent(final Command parent);
+        @Override
+        public void setResult(final Bookmark result) {
+            Command.this.result = result;
+        }
 
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         *     Previously this field was deprecated (on the basis that the completedAt is also held in
-         *     {@link Interaction.Execution#getCompletedAt()}). However, this property is now used in master/slave
-         *     replay scenarios which may query a persisted Command.
-         * </p>
-         *
-         * See also {@link Interaction#getCurrentExecution()} and
-         * {@link Interaction.Execution#setCompletedAt(Timestamp)}.
          */
-        void setCompletedAt(Timestamp completedAt);
-
+        @Override
+        public void setException(final Throwable exception) {
+            Command.this.exception = exception;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          *
-         *
          * <p>
-         *     Previously this field was deprecated (on the basis that the completedAt is also held in
-         *     {@link Interaction.Execution#getCompletedAt()}). However, this property is now used in master/slave
-         *     replay scenarios which may query a persisted Command.
+         * Hint that this {@link Command} has resulted in a change of state to the system.
+         * Implementations can use this to persist the command, for example.
          * </p>
-         *
-         * See also {@link Interaction#getCurrentExecution()} and
-         * {@link #setStartedAt(org.apache.isis.applib.services.clock.ClockService, org.apache.isis.applib.services.metrics.MetricsService)}.
          */
-        void setStartedAt(Timestamp startedAt);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>).
-         */
-        void setMemento(final String memento);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</t>).
-         */
-        void setTargetClass(String targetClass);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         */
-        void setPersistence(final CommandPersistence persistence);
-
-        /**
-         * Hint that this {@link Command} should be persisted, if possible.
-         *
-         * <p>
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * @see #getPersistence()
-         */
-        void setPersistHint(boolean persistHint);
-    }
+        public void setSystemStateChanged(boolean systemStateChanged) {
+            Command.this.systemStateChanged = systemStateChanged;
+        }
+    };
 
     /**
      * <b>NOT API</b>: intended to be called only by the framework.
      */
-    Internal internal();
+    public Updater updater() {
+        return UPDATER;
+    }
+
 
 // tag::refguide[]
+
 }
 // end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDefault.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDefault.java
deleted file mode 100644
index 069acd4..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDefault.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.command;
-
-import java.sql.Timestamp;
-import java.util.UUID;
-
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.util.ObjectContracts;
-import org.apache.isis.applib.util.ToString;
-
-import lombok.Getter;
-
-public class CommandDefault implements Command {
-
-    public CommandDefault() {
-        this.executor = Executor.OTHER;
-        this.uniqueId = UUID.randomUUID();
-    }
-
-    @Getter
-    private String memberIdentifier;
-
-    @Getter
-    private String targetClass;
-
-    @Getter
-    private String targetAction;
-
-    @Getter
-    private String arguments;
-
-    @Getter
-    private String memento;
-
-    @Getter
-    private Bookmark target;
-
-    @Getter
-    private Timestamp timestamp;
-
-    @Getter
-    private Timestamp startedAt;
-
-    @Getter
-    private Timestamp completedAt;
-
-    @Getter
-    private String user;
-
-    @Getter
-    private Executor executor;
-
-    @Getter
-    private CommandExecuteIn executeIn;
-
-    @Getter
-    private Command parent;
-
-    @Getter
-    private Bookmark result;
-
-    @Getter
-    private String exception;
-
-    @Getter
-    private UUID uniqueId;
-
-    @Getter
-    private CommandPersistence persistence;
-
-    @Getter
-    private boolean persistHint;
-
-    // -- toString
-
-    private static final ToString<CommandDefault> toString = ObjectContracts
-            .toString("startedAt", CommandDefault::getStartedAt)
-            .thenToString("user", CommandDefault::getUser)
-            .thenToString("memberIdentifier", CommandDefault::getMemberIdentifier)
-            .thenToString("target", CommandDefault::getTarget)
-            .thenToString("transactionId", CommandDefault::getUniqueId);
-
-    @Override
-    public String toString() {
-        return toString.toString(this);
-    }
-
-
-    // -- FRAMEWORK INTERNAL
-
-    private final Command.Internal INTERNAL = new Command.Internal() {
-        @Override
-        public void setMemberIdentifier(String actionIdentifier) {
-            CommandDefault.this.memberIdentifier = actionIdentifier;
-        }
-        @Override
-        public void setTargetClass(String targetClass) {
-            CommandDefault.this.targetClass = targetClass;
-        }
-        @Override
-        public void setTargetAction(String targetAction) {
-            CommandDefault.this.targetAction = targetAction;
-        }
-        @Override
-        public void setArguments(String arguments) {
-            CommandDefault.this.arguments = arguments;
-        }
-        @Override
-        public void setMemento(String memento) {
-            CommandDefault.this.memento = memento;
-        }
-        @Override
-        public void setTarget(Bookmark target) {
-            CommandDefault.this.target = target;
-        }
-        @Override
-        public void setTimestamp(Timestamp timestamp) {
-            CommandDefault.this.timestamp = timestamp;
-        }
-        @Override
-        public void setStartedAt(Timestamp startedAt) {
-            CommandDefault.this.startedAt = startedAt;
-        }
-        @Override
-        public void setCompletedAt(final Timestamp completed) {
-            CommandDefault.this.completedAt = completed;
-        }
-        @Override
-        public void setUser(String user) {
-            CommandDefault.this.user = user;
-        }
-        @Override
-        public void setParent(Command parent) {
-            CommandDefault.this.parent = parent;
-        }
-        @Override
-        public void setResult(final Bookmark result) {
-            CommandDefault.this.result = result;
-        }
-        @Override
-        public void setException(final String exceptionStackTrace) {
-            CommandDefault.this.exception = exceptionStackTrace;
-        }
-        @Override
-        public void setPersistence(CommandPersistence persistence) {
-            CommandDefault.this.persistence = persistence;
-        }
-        @Override
-        public void setPersistHint(boolean persistHint) {
-            CommandDefault.this.persistHint = persistHint;
-        }
-        @Override
-        public void setExecutor(Executor executor) {
-            CommandDefault.this.executor = executor;
-        }
-    };
-
-    @Override
-    public Command.Internal internal() {
-        return INTERNAL;
-    }
-
-}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandExecutorService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandExecutorService.java
index 56d029b..4fce410 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandExecutorService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandExecutorService.java
@@ -48,18 +48,27 @@ public interface CommandExecutorService {
      * Executes the specified command.
      *
      * @param sudoPolicy
-     * @param commandWithDto
+     * @param command
      * @return - any exception raised by the command.
      */
     // tag::refguide[]
-    void executeCommand(
-            SudoPolicy sudoPolicy,          // <.>
-            CommandWithDto commandWithDto   // <.>
+    Bookmark executeCommand(
+            SudoPolicy sudoPolicy,                  // <.>
+            Command command                         // <.>
     );
 
     Bookmark executeCommand(
-            CommandDto commandDto           // <.>
+            SudoPolicy sudoPolicy,                  // <.>
+            CommandDto commandDto,                  // <.>
+            CommandOutcomeHandler outcomeHandler);  // <.>
+
+    Bookmark executeCommand(
+            Command command                         // <.>
     );
 
+    Bookmark executeCommand(
+            CommandDto commandDto,                  // <.>
+            CommandOutcomeHandler outcomeHandler);  // <.>
+
 }
 // end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandOutcomeHandler.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandOutcomeHandler.java
new file mode 100644
index 0000000..ccd9530
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandOutcomeHandler.java
@@ -0,0 +1,24 @@
+package org.apache.isis.applib.services.command;
+
+import java.sql.Timestamp;
+
+import org.apache.isis.applib.services.bookmark.Bookmark;
+
+public interface CommandOutcomeHandler {
+
+    CommandOutcomeHandler NULL = new CommandOutcomeHandler() {
+        @Override public Timestamp getStartedAt() { return null; }
+        @Override public void setStartedAt(Timestamp startedAt) { }
+        @Override public void setCompletedAt(Timestamp completedAt) { }
+        @Override public void setResult(Bookmark resultBookmark) { }
+        @Override public void setException(Throwable throwable) { }
+    };
+
+    Timestamp getStartedAt();
+    void setStartedAt(Timestamp startedAt);
+
+    void setCompletedAt(Timestamp completedAt);
+
+    void setResult(Bookmark resultBookmark);
+    void setException(Throwable throwable);
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandContext.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandService.java
similarity index 53%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandContext.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandService.java
index cf71f1b..eae10f0 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandContext.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandService.java
@@ -18,10 +18,10 @@
  */
 package org.apache.isis.applib.services.command;
 
-import java.util.Optional;
+import java.sql.Timestamp;
+import java.util.List;
 
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
+import javax.inject.Inject;
 import javax.inject.Named;
 
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -31,44 +31,52 @@ import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.IsisInteractionScope;
 import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.services.command.Command.Executor;
+import org.apache.isis.applib.services.clock.ClockService;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.command.spi.CommandServiceListener;
+import org.apache.isis.applib.services.user.UserService;
 
-import lombok.Getter;
+import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
-/**
- * This service (API and implementation) provides access to context information about any {@link Command}.
- *
- * This implementation has no UI and there is only one implementation (this class) in applib, so it is annotated with
- * {@link org.apache.isis.applib.annotation.DomainService}.  This means that it is automatically registered and
- * available for use; no further configuration is required.
- */
-// tag::refguide[]
 @Service
-@Named("isisApplib.CommandContext")
+@Named("isisApplib.CommandService")
 @Order(OrderPrecedence.MIDPOINT)
 @Primary
 @Qualifier("Default")
-@IsisInteractionScope
 @Log4j2
-public class CommandContext {
-
-    @Getter
-    private Command command;
+// tag::refguide[]
+public class CommandService {
 
     // end::refguide[]
     /**
-     * <b>NOT API</b>: intended to be called only by the framework.
+     * &quot;Complete&quot; the command, providing an opportunity ot persist
+     * a memento of the command if the
+     * {@link Command#isSystemStateChanged() system state has changed}.
+     *
+     * <p>
+     *     The framework will automatically have set the {@link Command#getCompletedAt()} property.
+     * </p>
      */
-    public void setCommand(final Command command) {
-        this.command = command;
-    }
+    // tag::refguide[]
+    public void complete(final Command command) {   // <.>
+        // ...
+    // end::refguide[]
 
-    public Optional<Executor> getCurrentExecutor() {
-        return Optional.ofNullable(getCommand())
-                .map(Command::getExecutor);
-    }
+        if(command.getLogicalMemberIdentifier() == null) {
+            // eg if seed fixtures
+            return;
+        }
+
+        log.debug("complete: {}, systemStateChanged {}",
+                command.getLogicalMemberIdentifier(),
+                command.isSystemStateChanged());
 
     // tag::refguide[]
+        commandServiceListeners.forEach(x -> x.onComplete(command));
+    }
+
+    @Inject List<CommandServiceListener> commandServiceListeners;
+
 }
 // end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/package-info.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/package-info.java
index 99c057a..e044456 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/package-info.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/package-info.java
@@ -17,11 +17,4 @@
  *  under the License.
  */
 
-/**
- * The {@link org.apache.isis.applib.services.command.CommandContext} service is a request-scoped service that reifies
- * the invocation of an action on a domain object into an object itself. This reified information is encapsulated
- * within the {@link org.apache.isis.applib.services.command.Command} object.
- *
- *
- */
 package org.apache.isis.applib.services.command;
\ No newline at end of file
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/spi/CommandService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/spi/CommandService.java
deleted file mode 100644
index eb3abce..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/spi/CommandService.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.command.spi;
-
-import org.apache.isis.applib.services.command.Command;
-
-/**
- * Default factory service for {@link Command}s.
- */
-// tag::refguide[]
-public interface CommandService {
-
-    // end::refguide[]
-    /**
-     * Simply instantiates the appropriate instance of the {@link Command}.
-     *
-     * <p>
-     * Its members will be populated automatically by the framework (the {@link Command}'s
-     * {@link Command#getTimestamp()}, {@link Command#getUser()} and {@link Command#getUniqueId()}).
-     * </p>
-     */
-    // tag::refguide[]
-    Command create();                               // <.>
-    // end::refguide[]
-
-    /**
-     * Hint for this implementation to eagerly persist the {@link Command}s if possible; influences the behaviour
-     * of actions annotated to execute in the {@link org.apache.isis.applib.annotation.CommandExecuteIn#BACKGROUND}.
-     */
-    // tag::refguide[]
-    boolean persistIfPossible(Command command);     // <.>
-    // end::refguide[]
-
-    /**
-     * &quot;Complete&quot; the command, typically meaning to indicate that the command is completed, and to
-     * persist it if its {@link Command#getPersistence()} and {@link Command#isPersistHint() persistence hint}
-     * indicate that it should be.
-     *
-     * <p>
-     * However, not every implementation necessarily {@link #persistIfPossible(Command) supports persistence}.
-     *
-     * <p>
-     *     The framework will automatically have set the {@link Command#getCompletedAt()} property.
-     * </p>
-     */
-    // tag::refguide[]
-    void complete(final Command command);           // <.>
-}
-// end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandWithDto.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/spi/CommandServiceListener.java
similarity index 63%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandWithDto.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/command/spi/CommandServiceListener.java
index cd421ea..5d10793 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandWithDto.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/spi/CommandServiceListener.java
@@ -16,20 +16,25 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.command;
+package org.apache.isis.applib.services.command.spi;
 
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.schema.cmd.v2.CommandDto;
+import org.apache.isis.applib.services.command.Command;
 
+/**
+ * SPI
+ */
 // tag::refguide[]
-public interface CommandWithDto extends Command {
-
-    String USERDATA_KEY_TARGET_CLASS = "targetClass";
-    String USERDATA_KEY_TARGET_ACTION = "targetAction";
-    String USERDATA_KEY_ARGUMENTS = "arguments";
-    String USERDATA_KEY_RETURN_VALUE = "returnValue";
-    String USERDATA_KEY_EXCEPTION = "exception";
+public interface CommandServiceListener {
 
-    CommandDto asDto();
+    /**
+     * Notifies that the command has completed.
+     *
+     * <p>
+     *     This is an opportunity for implementations to process the command,
+     *     for example to persist a representation of it.
+     * </p>
+     */
+    // tag::refguide[]
+    void onComplete(final Command command);           // <.>
 }
 // end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/HasCommandDto.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/HasCommandDto.java
new file mode 100644
index 0000000..6c9d631
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/HasCommandDto.java
@@ -0,0 +1,12 @@
+package org.apache.isis.applib.services.commanddto;
+
+import org.apache.isis.schema.cmd.v2.CommandDto;
+
+/**
+ * Objects implementing this interface will be processed automatically by
+ * {@link org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto}.
+ */
+public interface HasCommandDto {
+
+    CommandDto getCommandDto();
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandDto.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandDto.java
new file mode 100644
index 0000000..6e1edfb
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandDto.java
@@ -0,0 +1,106 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.applib.services.commanddto.conmap;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.commanddto.HasCommandDto;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
+import org.apache.isis.applib.services.conmap.ContentMappingService;
+import org.apache.isis.applib.services.commanddto.processor.spi.CommandDtoProcessorService;
+import org.apache.isis.applib.services.metamodel.MetaModelService;
+import org.apache.isis.applib.util.schema.CommandDtoUtils;
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
+import org.apache.isis.schema.cmd.v2.CommandDto;
+import org.apache.isis.schema.common.v2.PeriodDto;
+
+import lombok.val;
+
+@Service
+@Named("isisApplib.ContentMappingServiceForCommandDto")
+@Order(OrderPrecedence.EARLY)
+@Primary
+@Qualifier("CommandDto")
+public class ContentMappingServiceForCommandDto implements ContentMappingService {
+
+    @Override
+    public Object map(Object object, final List<MediaType> acceptableMediaTypes) {
+        final boolean supported = Util.isSupported(CommandDto.class, acceptableMediaTypes);
+        if(!supported) {
+            return null;
+        }
+
+        return asProcessedDto(object);
+    }
+
+    CommandDto asProcessedDto(final Object object) {
+        val commandDto = asCommandDto(object);
+        return asProcessedDto(object, commandDto);
+    }
+
+    private CommandDto asCommandDto(Object object) {
+        if(object instanceof CommandDto) {
+            return (CommandDto) object;
+        }
+        if(object instanceof HasCommandDto) {
+            return ((HasCommandDto) object).getCommandDto();
+        }
+        return null;
+    }
+
+    private CommandDto asProcessedDto(final Object domainObject, CommandDto commandDto) {
+
+        // global processors
+        for (final CommandDtoProcessorService commandDtoProcessorService : commandDtoProcessorServices) {
+            commandDto = commandDtoProcessorService.process(domainObject, commandDto);
+            if(commandDto == null) {
+                // any processor could return null, effectively breaking the chain.
+                return null;
+            }
+        }
+
+        // specific processor for this specific member (action or property)
+        val logicalMemberId = commandDto.getMember().getLogicalMemberIdentifier();
+        final CommandDtoProcessor commandDtoProcessor =
+                metaModelService.commandDtoProcessorFor(logicalMemberId);
+        if (commandDtoProcessor == null) {
+            return commandDto;
+        }
+        return commandDtoProcessor.process(commandDto);
+    }
+
+
+    @Inject MetaModelService metaModelService;
+    @Inject List<CommandDtoProcessorService> commandDtoProcessorServices;
+
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/conmap/command/ContentMappingServiceForCommandsDto.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandsDto.java
similarity index 98%
rename from api/applib/src/main/java/org/apache/isis/applib/services/conmap/command/ContentMappingServiceForCommandsDto.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandsDto.java
index ade5070..4d491b2 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/conmap/command/ContentMappingServiceForCommandsDto.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/ContentMappingServiceForCommandsDto.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.conmap.command;
+package org.apache.isis.applib.services.commanddto.conmap;
 
 import java.util.List;
 
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/background/package-info.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/UserDataKeys.java
similarity index 65%
rename from api/applib/src/main/java/org/apache/isis/applib/services/background/package-info.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/UserDataKeys.java
index f63812d..4fc4e59 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/background/package-info.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/conmap/UserDataKeys.java
@@ -16,12 +16,19 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
+package org.apache.isis.applib.services.commanddto.conmap;
+
+import org.apache.isis.schema.cmd.v2.CommandDto;
+
+import lombok.experimental.UtilityClass;
 
 /**
- * The {@link org.apache.isis.applib.services.background.BackgroundService2} domain service, and also the companion
- * {@link org.apache.isis.applib.services.background.BackgroundCommandService2} SPI service, enable commands to be
- * persisted such that they may be invoked in the background
- *
- *
+ * Keys used in {@link CommandDto#getUserData()} to marshall the command's results
  */
-package org.apache.isis.applib.services.background;
\ No newline at end of file
+@UtilityClass
+public class UserDataKeys {
+
+    public static String RESULT = UserDataKeys.class.getName() + "#" + "RESULT";
+    public static String EXCEPTION = UserDataKeys.class.getName() + "#" + "EXCEPTION";
+
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessor.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessor.java
similarity index 57%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessor.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessor.java
index 3c7336e..a0706ad 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessor.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessor.java
@@ -16,34 +16,42 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.command;
+package org.apache.isis.applib.services.commanddto.processor;
 
-import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
+/**
+ * Refine (or possibly ignore) a command when replicating from primary
+ * to secondary.
+ */
 // tag::refguide[]
 public interface CommandDtoProcessor {
 
     // end::refguide[]
     /**
-     * Returning <tt>null</tt> means that the command's DTO is effectively excluded from any list.
-     * If replicating from master to slave, this allows commands that can't be replicated to be ignored.
-     * @param command
-     * @param commandDto
+     * The implementation can if necessary refine or alter the
+     * {@link CommandDto} to be replicated from primary to secondary.
+     *
+     * <p>
+     *     That said, the most common use case is to return <code>null</code>,
+     *     which results in the command effectively being ignore.
+     * </p>
+     *
+     * @param commandDto - to be processed
+     * @return <tt>null</tt> means that the command's DTO is effectively
+     *         excluded.
      */
     // tag::refguide[]
-    CommandDto process(final Command command, CommandDto commandDto);   // <.>
+    CommandDto process(CommandDto commandDto);   // <.>
 
     // end::refguide[]
     /**
-     * Convenience implementation to simply indicate that no DTO should be returned for a command,
-     * effectively ignoring it for replay purposes.
+     * Convenience implementation to simply indicate that no DTO should be
+     * returned for a command, effectively ignoring it for replication purposes.
      */
-    public static class Null implements CommandDtoProcessor {
+    class Null implements CommandDtoProcessor {
         @Override
-        public CommandDto process(
-                final Command command,
-                final CommandDto commandDto) {
+        public CommandDto process(final CommandDto commandDto) {
             return null;
         }
     }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessorForActionAbstract.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForActionAbstract.java
similarity index 90%
rename from api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessorForActionAbstract.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForActionAbstract.java
index 531c945..b887323 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessorForActionAbstract.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForActionAbstract.java
@@ -16,8 +16,9 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.command;
+package org.apache.isis.applib.services.commanddto.processor;
 
+import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.schema.cmd.v2.ActionDto;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.ParamDto;
@@ -27,9 +28,6 @@ import org.apache.isis.schema.cmd.v2.ParamsDto;
  * Convenience adapter for command processors for action invocations.
  */
 public abstract class CommandDtoProcessorForActionAbstract implements CommandDtoProcessor {
-    protected CommandDto asDto(final CommandWithDto commandWithDto) {
-        return commandWithDto.asDto();
-    }
     protected ActionDto getActionDto(final CommandDto commandDto) {
         return (ActionDto) commandDto.getMember();
     }
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForPropertyAbstract.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForPropertyAbstract.java
similarity index 88%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForPropertyAbstract.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForPropertyAbstract.java
index 6bc4d69..e833809 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForPropertyAbstract.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/CommandDtoProcessorForPropertyAbstract.java
@@ -16,8 +16,9 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.command;
+package org.apache.isis.applib.services.commanddto.processor;
 
+import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.PropertyDto;
 
@@ -26,9 +27,6 @@ import org.apache.isis.schema.cmd.v2.PropertyDto;
  */
 public abstract class CommandDtoProcessorForPropertyAbstract
 implements CommandDtoProcessor {
-    protected CommandDto asDto(final CommandWithDto commandWithDto) {
-        return commandWithDto.asDto();
-    }
     protected PropertyDto getPropertyDto(final CommandDto commandDto) {
         return (PropertyDto) commandDto.getMember();
     }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/conmap/command/spi/CommandDtoProcessorService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorService.java
similarity index 57%
rename from api/applib/src/main/java/org/apache/isis/applib/services/conmap/command/spi/CommandDtoProcessorService.java
rename to api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorService.java
index 48439df..c645d04 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/conmap/command/spi/CommandDtoProcessorService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorService.java
@@ -16,12 +16,17 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.conmap.command.spi;
+package org.apache.isis.applib.services.commanddto.processor.spi;
 
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandDtoProcessor;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandDto;
+import javax.annotation.Nullable;
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
 /**
@@ -32,7 +37,16 @@ import org.apache.isis.schema.cmd.v2.CommandDto;
 // tag::refguide[]
 public interface CommandDtoProcessorService {
 
-    CommandDto process(final Command command, CommandDto commandDto);
+    /**
+     * @param domainObject - is the target that acts as the source of the
+     *                       {@link CommandDto}.
+     * @param commandDto - is either <code>null</code>, or is passed from a
+     *                     previous implementation for further refinement.
+     * @return
+     */
+    CommandDto process(final Object domainObject, @Nullable final CommandDto commandDto);
+
 
 }
 // end::refguide[]
+
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorServiceIdentity.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorServiceIdentity.java
new file mode 100644
index 0000000..aee41b0
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/commanddto/processor/spi/CommandDtoProcessorServiceIdentity.java
@@ -0,0 +1,23 @@
+package org.apache.isis.applib.services.commanddto.processor.spi;
+
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.schema.cmd.v2.CommandDto;
+
+/**
+ * At least one implementation is required.
+ */
+@Service
+@Named("isisApplib.CommandDtoProcessorServiceIdentity")
+@Order(OrderPrecedence.LAST)
+public class CommandDtoProcessorServiceIdentity implements CommandDtoProcessorService {
+
+    @Override
+    public CommandDto process(final Object domainObject, final CommandDto commandDto) {
+        return commandDto;
+    }
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandDto.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandDto.java
deleted file mode 100644
index a4cea23..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandDto.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.conmap.command;
-
-import java.sql.Timestamp;
-import java.util.List;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.ws.rs.core.MediaType;
-
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.annotation.Primary;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-
-import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandDtoProcessor;
-import org.apache.isis.applib.services.command.CommandWithDto;
-import org.apache.isis.applib.services.conmap.ContentMappingService;
-import org.apache.isis.applib.services.conmap.command.spi.CommandDtoProcessorService;
-import org.apache.isis.applib.services.metamodel.MetaModelService;
-import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-import org.apache.isis.schema.common.v2.PeriodDto;
-
-@Service
-@Named("isisApplib.ContentMappingServiceForCommandDto")
-@Order(OrderPrecedence.EARLY)
-@Primary
-@Qualifier("CommandDto")
-public class ContentMappingServiceForCommandDto implements ContentMappingService {
-
-    @Override
-    public Object map(Object object, final List<MediaType> acceptableMediaTypes) {
-        final boolean supported = Util.isSupported(CommandDto.class, acceptableMediaTypes);
-        if(!supported) {
-            return null;
-        }
-
-        return asProcessedDto(object);
-    }
-
-    /**
-     * Not part of the {@link ContentMappingService} API.
-     */
-    public CommandDto map(final CommandWithDto commandWithDto) {
-        return asProcessedDto(commandWithDto);
-    }
-
-    CommandDto asProcessedDto(final Object object) {
-        if (!(object instanceof CommandWithDto)) {
-            return null;
-        }
-        final CommandWithDto commandWithDto = (CommandWithDto) object;
-        return asProcessedDto(commandWithDto);
-    }
-
-    private CommandDto asProcessedDto(final CommandWithDto commandWithDto) {
-        if(commandWithDto == null) {
-            return null;
-        }
-        CommandDto commandDto = commandWithDto.asDto();
-
-        // global processors
-        for (final CommandDtoProcessorService commandDtoProcessorService : commandDtoProcessorServices) {
-            commandDto = commandDtoProcessorService.process(commandWithDto, commandDto);
-            if(commandDto == null) {
-                // any processor could return null, effectively breaking the chain.
-                return null;
-            }
-        }
-
-        // specific processors for this specific member (action or property)
-        final CommandDtoProcessor commandDtoProcessor =
-                metaModelService.commandDtoProcessorFor(commandDto.getMember().getLogicalMemberIdentifier());
-        if (commandDtoProcessor == null) {
-            return commandDto;
-        }
-        return commandDtoProcessor.process(commandWithDto, commandDto);
-    }
-
-
-    /**
-     * Uses the SPI infrastructure to copy over standard properties from {@link Command} to {@link CommandDto}.
-     */
-    @Service
-    @Named("isisApplib.ContentMappingServiceForCommandDto.CopyOverFromCommand")
-    // specify quite a high priority since custom processors will probably want to run after this one
-    // (but can choose to run before if they wish)
-    @Order(OrderPrecedence.EARLY)
-    @Qualifier("Command")
-    public static class CopyOverFromCommand implements CommandDtoProcessorService {
-
-        @Override
-        public CommandDto process(final Command command, CommandDto commandDto) {
-
-            // for some reason this isn't being persisted initially, so patch it in.  TODO: should fix this
-            commandDto.setUser(command.getUser());
-
-            // the timestamp field was only introduced in v1.4 of cmd.xsd, so there's no guarantee
-            // it will have been populated.  We therefore copy the value in from CommandWithDto entity.
-            if(commandDto.getTimestamp() == null) {
-                final Timestamp timestamp = command.getTimestamp();
-                commandDto.setTimestamp(JavaSqlTimestampXmlGregorianCalendarAdapter.print(timestamp));
-            }
-
-            CommandDtoUtils.setUserData(commandDto,
-                    CommandWithDto.USERDATA_KEY_TARGET_CLASS, command.getTargetClass());
-            CommandDtoUtils.setUserData(commandDto,
-                    CommandWithDto.USERDATA_KEY_TARGET_ACTION, command.getTargetAction());
-            CommandDtoUtils.setUserData(commandDto,
-                    CommandWithDto.USERDATA_KEY_ARGUMENTS, command.getArguments());
-
-            final Bookmark result = command.getResult();
-            CommandDtoUtils.setUserData(commandDto,
-                    CommandWithDto.USERDATA_KEY_RETURN_VALUE, result != null ? result.toString() : null);
-            // knowing whether there was an exception is on the master is used to determine whether to
-            // continue when replayed on the slave if an exception occurs there also
-            CommandDtoUtils.setUserData(commandDto,
-                    CommandWithDto.USERDATA_KEY_EXCEPTION, command.getException());
-
-            PeriodDto timings = CommandDtoUtils.timingsFor(commandDto);
-            timings.setStartedAt(JavaSqlTimestampXmlGregorianCalendarAdapter.print(command.getStartedAt()));
-            timings.setCompletedAt(JavaSqlTimestampXmlGregorianCalendarAdapter.print(command.getCompletedAt()));
-
-            return commandDto;
-        }
-    }
-
-
-    @Inject
-    MetaModelService metaModelService;
-
-    @Inject
-    List<CommandDtoProcessorService> commandDtoProcessorServices;
-
-}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/exceprecog/ExceptionRecognizer.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/exceprecog/ExceptionRecognizer.java
index ca322a6..f51628f 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/exceprecog/ExceptionRecognizer.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/exceprecog/ExceptionRecognizer.java
@@ -158,20 +158,33 @@ public interface ExceptionRecognizer {
         public String toMessage(@Nullable TranslationService translationService) {
             // end::refguide-2[]
 
-            val categoryLiteral = translationService!=null
-                    ? translationService.translate(
-                    ExceptionRecognizer.Category.class.getName(), getCategory().getFriendlyName())
-                    : getCategory().getFriendlyName();
-
-            val reasonLiteral = translationService!=null
-                    ? translationService.translate(
-                    ExceptionRecognizer.Recognition.class.getName(), getReason())
-                    : getReason();
+            val categoryLiteral = translate(getCategory().getFriendlyName(), translationService);
+            val reasonLiteral = translate(getReason(), translationService);
 
             return String.format("[%s]: %s", categoryLiteral, reasonLiteral);
             // tag::refguide-2[]
             // ...
         }
+        
+        public String toMessageNoCategory(@Nullable TranslationService translationService) {
+            // end::refguide-2[]
+
+            val reasonLiteral = translate(getReason(), translationService);
+            return String.format("%s", reasonLiteral);
+            // tag::refguide-2[]
+            // ...
+        }
+        
+        private static String translate(
+                @Nullable String x, 
+                @Nullable TranslationService translationService) {
+            if(x==null || translationService==null) {
+                return x;
+            }
+            return translationService.translate(
+                    ExceptionRecognizer.Recognition.class.getName(), x);
+        }
+        
     }
     // end::refguide-2[]
 
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/Interaction.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/Interaction.java
index 7f12c39..3e2de34 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/Interaction.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/Interaction.java
@@ -30,6 +30,7 @@ import java.util.concurrent.atomic.LongAdder;
 import org.apache.isis.applib.events.domain.AbstractDomainEvent;
 import org.apache.isis.applib.events.domain.ActionDomainEvent;
 import org.apache.isis.applib.events.domain.PropertyDomainEvent;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
 import org.apache.isis.applib.services.HasUniqueId;
 import org.apache.isis.applib.services.clock.ClockService;
 import org.apache.isis.applib.services.command.Command;
@@ -49,7 +50,6 @@ import org.apache.isis.schema.ixn.v2.ObjectCountsDto;
 import org.apache.isis.schema.ixn.v2.PropertyEditDto;
 
 import lombok.Getter;
-import lombok.Setter;
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
@@ -82,8 +82,17 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 public class Interaction implements HasUniqueId {
 
-    @Getter @Setter
-    private UUID uniqueId;                          // <.>
+    public Interaction(final Command command) {
+        this.command = command;
+    }
+
+    @Getter
+    private Command command;                        // <.>
+
+    @Override
+    public UUID getUniqueId() {                     // <.>
+        return command.getUniqueId();
+    }
 
     // end::refguide[]
     private final List<Execution<?,?>> executionGraphs = _Lists.newArrayList();
@@ -158,16 +167,31 @@ public class Interaction implements HasUniqueId {
      * Use the provided {@link MemberExecutor} to invoke an action, with the provided
      * {@link ActionInvocation} capturing the details of said action.
      * </p>
+     *
+     * <p>
+     *     Because this both pushes an {@link Interaction.Execution} to
+     *     represent the action invocation and then pops it, that completed
+     *     execution is accessible at {@link Interaction#getPriorExecution()}.
+     * </p>
      */
     public Object execute(
             final MemberExecutor<ActionInvocation> memberExecutor,
             final ActionInvocation actionInvocation,
             final ClockService clockService,
-            final MetricsService metricsService) {
+            final MetricsService metricsService,
+            final Command command) {
 
-        push(actionInvocation);
+        pushAndStart(actionInvocation, clockService, metricsService, command);
+        try {
+            return executeInternal(memberExecutor, actionInvocation);
+        } finally {
+            popAndComplete(clockService, metricsService);
+        }
+    }
 
-        return executeInternal(memberExecutor, actionInvocation, clockService, metricsService);
+    private void pushAndStart(ActionInvocation actionInvocation, ClockService clockService, MetricsService metricsService, Command command) {
+        push(actionInvocation);
+        start(actionInvocation, clockService, metricsService, command);
     }
 
     /**
@@ -177,51 +201,50 @@ public class Interaction implements HasUniqueId {
      * Use the provided {@link MemberExecutor} to edit a property, with the provided
      * {@link PropertyEdit} capturing the details of said property edit.
      * </p>
+     *
+     * <p>
+     *     Because this both pushes an {@link Interaction.Execution} to
+     *     represent the property edit and then pops it, that completed
+     *     execution is accessible at {@link Interaction#getPriorExecution()}.
+     * </p>
      */
     public Object execute(
             final MemberExecutor<PropertyEdit> memberExecutor,
             final PropertyEdit propertyEdit,
             final ClockService clockService,
-            final MetricsService metricsService) {
+            final MetricsService metricsService,
+            final Command command) {
 
         push(propertyEdit);
-
-        return executeInternal(memberExecutor, propertyEdit, clockService, metricsService);
+        start(propertyEdit, clockService, metricsService, command);
+        try {
+            return executeInternal(memberExecutor, propertyEdit);
+        } finally {
+            popAndComplete(clockService, metricsService);
+        }
     }
 
-    private <T extends Execution<?,?>> Object executeInternal(
-            final MemberExecutor<T> memberExecutor,
-            final T execution,
-            final ClockService clockService,
-            final MetricsService metricsService) {
-
-        // as a convenience, since in all cases we want the command to start when the first 
-        // interaction executes, we populate the command here.
+    private <T extends Execution<?,?>> Object executeInternal(MemberExecutor<T> memberExecutor, T execution) {
 
         try {
-            try {
-                Object result = memberExecutor.execute(execution);
-                execution.setReturned(result);
-                return result;
-            } catch (Exception ex) {
-
-                //TODO there is an issue with exceptions getting swallowed, unless this is fixed,
-                // we rather print all of them, no matter whether recognized or not later on
-                // examples are IllegalArgument- or NullPointer- exceptions being swallowed when using the
-                // WrapperFactory utilizing async calls
-                log.error("failed to execute an interaction", ex);
-
-                // just because an exception has thrown, does not mean it is that significant;
-                // it could be that it is recognized by an ExceptionRecognizer and is not severe
-                // eg. unique index violation in the DB
-                getCurrentExecution().setThrew(ex);
-
-                // propagate (as in previous design); caller will need to trap and decide
-                throw ex;
-            }
-        } finally {
-            final Timestamp completedAt = clockService.nowAsJavaSqlTimestamp();
-            pop(completedAt, metricsService);
+            Object result = memberExecutor.execute(execution);
+            execution.setReturned(result);
+            return result;
+        } catch (Exception ex) {
+
+            //TODO there is an issue with exceptions getting swallowed, unless this is fixed,
+            // we rather print all of them, no matter whether recognized or not later on
+            // examples are IllegalArgument- or NullPointer- exceptions being swallowed when using the
+            // WrapperFactory utilizing async calls
+            log.error("failed to execute an interaction", ex);
+
+            // just because an exception has thrown, does not mean it is that significant;
+            // it could be that it is recognized by an ExceptionRecognizer and is not severe
+            // eg. unique index violation in the DB
+            getCurrentExecution().setThrew(ex);
+
+            // propagate (as in previous design); caller will need to trap and decide
+            throw ex;
         }
     }
 
@@ -252,6 +275,20 @@ public class Interaction implements HasUniqueId {
         return execution;
     }
 
+    private void start(
+            final Interaction.Execution<?,?> execution,
+            final ClockService clockService,
+            final MetricsService metricsService,
+            final Command command) {
+        // set the startedAt (and update command if this is the top-most member execution)
+        // (this isn't done within Interaction#execute(...) because it requires the DTO
+        // to have been set on the current execution).
+        val startedAt = execution.start(clockService, metricsService);
+        if(command.getStartedAt() == null) {
+            command.updater().setStartedAt(startedAt);
+        }
+    }
+
     /**
      * <b>NOT API</b>: intended to be called only by the framework.
      *
@@ -260,13 +297,16 @@ public class Interaction implements HasUniqueId {
      * from the stack of events held by the command.
      * </p>
      */
-    private Execution<?,?> pop(
-            final Timestamp completedAt,
+    private Execution<?,?> popAndComplete(
+            final ClockService clockService,
             final MetricsService metricsService) {
+
         if(currentExecution == null) {
             throw new IllegalStateException("No current execution to pop");
         }
         final Execution<?,?> popped = currentExecution;
+
+        final Timestamp completedAt = clockService.nowAsJavaSqlTimestamp();
         popped.setCompletedAt(completedAt, metricsService);
 
         moveCurrentTo(currentExecution.getParent());
@@ -385,8 +425,18 @@ public class Interaction implements HasUniqueId {
         // tag::refguide-2[]
         @Getter
         private final String targetMember;
-
         // end::refguide-2[]
+
+
+        /**
+         * Captures metrics before the Execution Dto is present.
+         */
+        private int numberObjectsLoadedBefore;
+        /**
+         * Captures metrics before the Execution Dto is present.
+         */
+        private int numberObjectsDirtiedBefore;
+
         protected Execution(
                 final Interaction interaction,
                 final InteractionType interactionType,
@@ -574,15 +624,8 @@ public class Interaction implements HasUniqueId {
                         final int numberObjectsDirtied) {
 
                     execution.startedAt = timestamp;
-
-                    final MetricsDto metricsDto = metricsFor(execution);
-
-                    final PeriodDto periodDto = timingsFor(metricsDto);
-                    periodDto.setStartedAt(JavaSqlTimestampXmlGregorianCalendarAdapter.print(timestamp));
-
-                    final ObjectCountsDto objectCountsDto = objectCountsFor(metricsDto);
-                    numberObjectsLoadedFor(objectCountsDto).setBefore(numberObjectsLoaded);
-                    numberObjectsDirtiedFor(objectCountsDto).setBefore(numberObjectsDirtied);
+                    execution.numberObjectsLoadedBefore = numberObjectsLoaded;
+                    execution.numberObjectsDirtiedBefore = numberObjectsLoaded;
                 }
 
                 // tag::refguide-2a[]
@@ -602,9 +645,13 @@ public class Interaction implements HasUniqueId {
                     final MetricsDto metricsDto = metricsFor(execution);
 
                     final PeriodDto periodDto = timingsFor(metricsDto);
-                    periodDto.setCompletedAt(JavaSqlTimestampXmlGregorianCalendarAdapter.print(timestamp));
+                    periodDto.setStartedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(execution.startedAt));
+                    periodDto.setCompletedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(execution.completedAt));
 
                     final ObjectCountsDto objectCountsDto = objectCountsFor(metricsDto);
+                    numberObjectsLoadedFor(objectCountsDto).setBefore(execution.numberObjectsLoadedBefore);
+                    numberObjectsDirtiedFor(objectCountsDto).setBefore(execution.numberObjectsDirtiedBefore);
+
                     numberObjectsLoadedFor(objectCountsDto).setAfter(numberObjectsLoaded);
                     numberObjectsDirtiedFor(objectCountsDto).setAfter(numberObjectsDirtied);
                 }
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/InteractionContext.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/InteractionContext.java
index b4a94b4..de41f1e 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/InteractionContext.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/iactn/InteractionContext.java
@@ -18,10 +18,10 @@
  */
 package org.apache.isis.applib.services.iactn;
 
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
+import javax.inject.Inject;
 import javax.inject.Named;
 
+import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Primary;
 import org.springframework.core.annotation.Order;
@@ -30,8 +30,14 @@ import org.springframework.stereotype.Service;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.IsisInteractionScope;
 import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.services.TransactionScopeListener;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.inject.ServiceInjector;
+import org.apache.isis.applib.services.metrics.MetricsService;
 
 import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
 /**
@@ -44,14 +50,18 @@ import lombok.extern.log4j.Log4j2;
 // tag::refguide[]
 @Service
 @Named("isisApplib.InteractionContext")
-@Order(OrderPrecedence.MIDPOINT)
+@Order(OrderPrecedence.EARLY - 10) // before ChangedObjectService
 @Primary
 @Qualifier("Default")
 @IsisInteractionScope
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
 @Log4j2
-public class InteractionContext {
+public class InteractionContext implements TransactionScopeListener, DisposableBean {
 
     // end::refguide[]
+
+    private final MetricsService metricsService;
+
     /**
      * The currently active {@link Interaction} for this thread.
      */
@@ -68,5 +78,19 @@ public class InteractionContext {
     }
 
     // tag::refguide[]
+
+    @Override
+    public void onTransactionEnded() {
+        val command = getInteraction().getCommand();
+        command.updater().setSystemStateChanged(
+                command.isSystemStateChanged() ||
+                        metricsService.numberObjectsDirtied() > 0);
+    }
+
+    @Override
+    public void destroy() throws Exception {
+        setInteraction(null);
+    }
+
 }
 // end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/jaxb/JaxbService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/jaxb/JaxbService.java
index 3ccc8ba..4148840 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/jaxb/JaxbService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/jaxb/JaxbService.java
@@ -26,6 +26,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
+import javax.annotation.Nullable;
 import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
@@ -39,6 +40,8 @@ import org.apache.isis.commons.internal.base._Casts;
 import org.apache.isis.commons.internal.base._NullSafe;
 import org.apache.isis.commons.internal.collections._Maps;
 
+import lombok.NonNull;
+
 // tag::refguide[]
 public interface JaxbService {
 
@@ -131,13 +134,20 @@ public interface JaxbService {
         }
 
         protected Object internalFromXml(
-                final JAXBContext jaxbContext,
-                final String xml,
-                final Map<String, Object> unmarshallerProperties) throws JAXBException {
+                @NonNull final JAXBContext jaxbContext,
+                @Nullable final String xml,
+                @Nullable final Map<String, Object> unmarshallerProperties) throws JAXBException {
+            
+            if(xml==null) {
+                return null;
+            }
+            
             final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
 
-            for (Map.Entry<String, Object> entry : unmarshallerProperties.entrySet()) {
-                unmarshaller.setProperty(entry.getKey(), entry.getValue());
+            if(unmarshallerProperties!=null) {
+                for (Map.Entry<String, Object> entry : unmarshallerProperties.entrySet()) {
+                    unmarshaller.setProperty(entry.getKey(), entry.getValue());
+                }
             }
 
             configure(unmarshaller);
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/jaxb/JaxbServiceDefault.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/jaxb/JaxbServiceDefault.java
index ae574a6..e990777 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/jaxb/JaxbServiceDefault.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/jaxb/JaxbServiceDefault.java
@@ -37,8 +37,8 @@ import org.springframework.stereotype.Service;
 import org.apache.isis.applib.NonRecoverableException;
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.domain.DomainObjectList;
-import org.apache.isis.applib.jaxbadapters.PersistentEntitiesAdapter;
-import org.apache.isis.applib.jaxbadapters.PersistentEntityAdapter;
+import org.apache.isis.applib.jaxb.PersistentEntitiesAdapter;
+import org.apache.isis.applib.jaxb.PersistentEntityAdapter;
 import org.apache.isis.applib.services.inject.ServiceInjector;
 import org.apache.isis.applib.services.metamodel.MetaModelService;
 import org.apache.isis.applib.services.registry.ServiceRegistry;
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/metamodel/MetaModelService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/metamodel/MetaModelService.java
index ac5770a..840ea50 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/metamodel/MetaModelService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/metamodel/MetaModelService.java
@@ -26,7 +26,7 @@ import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties.S
 
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.command.CommandDtoProcessor;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.schema.metamodel.v2.MetamodelDto;
 
@@ -79,7 +79,7 @@ public interface MetaModelService {
     BeanSort sortOf(Bookmark bookmark, Mode mode);      // <.>
 
     CommandDtoProcessor commandDtoProcessorFor(         // <.>
-                            String memberIdentifier);
+                            String logicalMemberIdentifier);
 
     // end::refguide[]
     // tag::refguide-1[]
@@ -92,7 +92,7 @@ public interface MetaModelService {
         STRICT,
         // end::refguide-1[]
         /**
-         * If the {@link #sortOf(Class, Mode) sort of} object type is unknown, then return {@link Sort#UNKNOWN}.
+         * If the {@link #sortOf(Class, Mode) sort of} object type is unknown, then return {@link BeanSort#UNKNOWN}.
          */
         // tag::refguide-1[]
         RELAXED
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/queryresultscache/QueryResultsCache.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/queryresultscache/QueryResultsCache.java
index 6e22285..c0e6a75 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/queryresultscache/QueryResultsCache.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/queryresultscache/QueryResultsCache.java
@@ -21,6 +21,8 @@ package org.apache.isis.applib.services.queryresultscache;
 import java.util.Arrays;
 import java.util.concurrent.Callable;
 
+import org.springframework.beans.factory.DisposableBean;
+
 import org.apache.isis.applib.services.MethodReferences;
 
 import lombok.Data;
@@ -38,7 +40,7 @@ import lombok.Getter;
  * available for use; no further configuration is required.
  */
 // tag::refguide[]
-public interface QueryResultsCache {
+public interface QueryResultsCache extends DisposableBean {
 
     <T> T execute(                                      // <.>
             Callable<T> callable,
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/scratchpad/Scratchpad.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/scratchpad/Scratchpad.java
index 8f241ad..366698e 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/scratchpad/Scratchpad.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/scratchpad/Scratchpad.java
@@ -24,6 +24,7 @@ import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 import javax.inject.Named;
 
+import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Primary;
 import org.springframework.core.annotation.Order;
@@ -46,7 +47,8 @@ import lombok.extern.log4j.Log4j2;
  * available for use; no further configuration is required.
  */
 // tag::refguide[]
-public interface Scratchpad {
+public interface Scratchpad
+        extends DisposableBean {
 
     // end::refguide[]
     /**
@@ -62,12 +64,6 @@ public interface Scratchpad {
     // tag::refguide[]
     public void put(Object key, Object value);
 
-    // end::refguide[]
-    /**
-     * Clear any user data.
-     */
-    // tag::refguide[]
-    public void clear();
 
 }
 // end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/sudo/SudoService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/sudo/SudoService.java
index f5e94e2..621828b 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/sudo/SudoService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/sudo/SudoService.java
@@ -20,9 +20,8 @@
 package org.apache.isis.applib.services.sudo;
 
 import java.util.List;
-import java.util.concurrent.Callable;
+import java.util.function.Supplier;
 
-import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.user.UserService;
 
 /**
@@ -65,7 +64,7 @@ public interface SudoService {
     // tag::refguide[]
     <T> T sudo(                                             // <.>
             String username,
-            final Callable<T> callable);
+            final Supplier<T> supplier);
 
     // end::refguide[]
     /**
@@ -83,7 +82,7 @@ public interface SudoService {
     // tag::refguide[]
     <T> T sudo(                                             // <.>
             String username, List<String> roles,
-            final Callable<T> callable);
+            final Supplier<T> supplier);
 
     // end::refguide[]
 
@@ -96,7 +95,7 @@ public interface SudoService {
         // end::refguide-1[]
         /**
          * Any implementation of the {@link SudoService} should call this method on all implementations of the
-         * {@link Spi} service whenever {@link SudoService#sudo(String, List, Callable)} (or its overloads)
+         * {@link Spi} service whenever {@link SudoService#sudo(String, List, Supplier)} (or its overloads)
          * is called.
          *
          * <p>
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/tablecol/TableColumnOrderForCollectionTypeAbstract.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/tablecol/TableColumnOrderForCollectionTypeAbstract.java
new file mode 100644
index 0000000..33bc35d
--- /dev/null
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/tablecol/TableColumnOrderForCollectionTypeAbstract.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.applib.services.tablecol;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+
+// tag::refguide[]
+@RequiredArgsConstructor
+public abstract class TableColumnOrderForCollectionTypeAbstract<T>
+        implements TableColumnOrderService {
+
+    private final Class<T> collectionType;
+
+    public final List<String> orderParented(
+            final Object parent,
+            final String collectionId,
+            final Class<?> collectionType,
+            final List<String> propertyIds) {
+        if (! this.collectionType.isAssignableFrom(collectionType)) {
+            return propertyIds;
+        }
+        return orderParented(parent, collectionId, propertyIds);
+    }
+
+    protected List<String> orderParented(
+            final Object parent,
+            final String collectionId,
+            final List<String> propertyIds) {
+        return propertyIds;
+    }
+
+    public final List<String> orderStandalone(
+            final Class<?> collectionType,
+            final List<String> propertyIds) {
+        if (! this.collectionType.isAssignableFrom(collectionType)) {
+            return propertyIds;
+        }
+        return orderStandalone(propertyIds);
+    }
+
+    protected List<String> orderStandalone(
+            final List<String> propertyIds) {
+        return propertyIds;
+    }
+
+}
+// end::refguide[]
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControl.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControl.java
index d5fccec..fa6ab86 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControl.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControl.java
@@ -39,14 +39,18 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
 
-    public static AsyncControl<Void> control() {                        // <.>
-        return new AsyncControl<>();
+    public static AsyncControl<Void> returningVoid() {                        // <.>
+        return new AsyncControl<>(Void.class);
     }
-    public static <X> AsyncControl<X> control(final Class<X> clazz) {   // <.>
-        return new AsyncControl<>();
+    public static <X> AsyncControl<X> returning(final Class<X> cls) {     // <.>
+        return new AsyncControl<X>(cls);
     }
 
-    private AsyncControl() {
+    @Getter
+    private final Class<R> returnType;                                  // <.>
+
+    private AsyncControl(final Class<R> returnType) {
+        this.returnType = returnType;
         with(exception -> {                                             // <.>
             log.error(logMessage(), exception);
             return null;
@@ -107,9 +111,8 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
      * Contains the result of the invocation.  However, if an entity is returned, then the object is automatically
      * detached because the persistence session within which it was obtained will have been closed already.
      */
-    @Setter(AccessLevel.PACKAGE)
     // tag::refguide[]
-    @Getter
+    @Getter @Setter
     private Future<R> future;                                           // <.>
 
     // end::refguide[]
@@ -127,6 +130,7 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
         }
         return buf.toString();
     }
+
     // tag::refguide[]
     // ...
 }
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControlService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControlService.java
deleted file mode 100644
index c112868..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/AsyncControlService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.wrapper.control;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.Future;
-
-import org.springframework.stereotype.Component;
-
-import org.apache.isis.applib.services.bookmark.Bookmark;
-
-import lombok.val;
-
-/**
- * For framework internal use.
- */
-@Component
-public class AsyncControlService {
-
-    public <R> AsyncControl<R> init(AsyncControl<R> asyncControl, Method method, Bookmark bookmark) {
-        asyncControl.setMethod(method);
-        asyncControl.setBookmark(bookmark);
-        return asyncControl;
-    }
-
-    public <R> AsyncControl<R> update(AsyncControl<R> asyncControl, Future<R> future) {
-        asyncControl.setFuture(future);
-        return asyncControl;
-    }
-
-    public <R> boolean shouldCheckRules(AsyncControl<R> asyncControl) {
-        val executionModes = asyncControl.getExecutionModes();
-        val skipRules = executionModes.contains(ExecutionMode.SKIP_RULE_VALIDATION);
-        return !skipRules;
-    }
-}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/ControlAbstract.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/ControlAbstract.java
index da06560..885c150 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/ControlAbstract.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/wrapper/control/ControlAbstract.java
@@ -20,6 +20,7 @@ package org.apache.isis.applib.services.wrapper.control;
 
 import java.lang.reflect.Method;
 import java.util.EnumSet;
+import java.util.Optional;
 
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.commons.collections.ImmutableEnumSet;
@@ -27,7 +28,6 @@ import org.apache.isis.commons.internal.base._Casts;
 
 import lombok.AccessLevel;
 import lombok.Getter;
-import lombok.NonNull;
 import lombok.Setter;
 
 // tag::refguide[]
@@ -38,17 +38,15 @@ public class ControlAbstract<T extends ControlAbstract<T>> {
     }
 
     /**
-     * Set by framework.
+     * Set by framework; simply used for logging purposes.
      */
-    @Setter(AccessLevel.PACKAGE)
-    @Getter(AccessLevel.PACKAGE)
+    @Getter(AccessLevel.PACKAGE) @Setter
     private Method method;
 
     /**
-     * Set by framework.
+     * Set by framework; simply used for logging purposes.
      */
-    @Setter(AccessLevel.PACKAGE)
-    @Getter(AccessLevel.PACKAGE)
+    @Getter(AccessLevel.PACKAGE) @Setter
     private Bookmark bookmark;
 
     // tag::refguide[]
@@ -67,14 +65,13 @@ public class ControlAbstract<T extends ControlAbstract<T>> {
         // tag::refguide[]
         // ...
     }
-
-    // end::refguide[]
-    /**
-     * Initialized in constructor.
-     */
-    // tag::refguide[]
-    @Getter @NonNull
-    private ExceptionHandler exceptionHandler;                  // <.>
+    
+    private ExceptionHandler exceptionHandler;
+   
+    public Optional<ExceptionHandler> getExceptionHandler() { // <.>
+        return Optional.ofNullable(exceptionHandler);
+    }
+                      
     public T with(ExceptionHandler exceptionHandler) {
         // end::refguide[]
         this.exceptionHandler = exceptionHandler;
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xactn/TransactionService.java b/api/applib/src/main/adoc/modules/applib-svc/examples/services/xactn/TransactionService.java
index fb5d805..f308df4 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/xactn/TransactionService.java
+++ b/api/applib/src/main/adoc/modules/applib-svc/examples/services/xactn/TransactionService.java
@@ -85,23 +85,8 @@ public interface TransactionService {
      */
     // tag::refguide[]
     <T> T executeWithinTransaction(Supplier<T> task);       // <.>
-
     // end::refguide[]
 
-//   the executeWithinNewTransaction at time of writing is incorrect (doesn't create a new xactn).
-//   not sure there's any need for these additional methods?
-//
-//    /**
-//     * Runs given {@code task} within its own (new) transactional boundary.
-//     * @param task
-//     */
-//    void executeWithinNewTransaction(Runnable task);        // <.>
-//
-//    /**
-//     * Runs given {@code task} within its own (new) transactional boundary.
-//     * @param task
-//     */
-//    <T> T executeWithinNewTransaction(Supplier<T> task);    // <.>
 
     // tag::refguide[]
 }
diff --git a/api/applib/src/main/adoc/modules/applib-svc/pages/BookmarkService.adoc b/api/applib/src/main/adoc/modules/applib-svc/pages/BookmarkService.adoc
index 65e7215..581b8b2 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/pages/BookmarkService.adoc
+++ b/api/applib/src/main/adoc/modules/applib-svc/pages/BookmarkService.adoc
@@ -17,7 +17,6 @@ For example the xref:refguide:applib-svc:AuditerService.adoc[`AuditerService`] u
 
 Serialized form of bookmarks also appear within  xref:refguide:schema:about.adoc[schema] instances, for example as used by xref:refguide:applib-svc:CommandService.adoc[`CommandService`] and the xref:refguide:applib-svc:PublisherService.adoc[`PublisherService`].
 
-//Bookmarks are also used by the (non-ASF) link:https://platform.incode.org[Incode Platform^]'s command module's implementation of  xref:refguide:applib-svc:_BackgroundCommandService.adoc[`BackgroundCommandService`], which uses a bookmark to capture the target object on which an action will be invoked subsequently.
 
 
 == API
diff --git a/api/applib/src/main/adoc/modules/applib-svc/pages/CommandContext.adoc b/api/applib/src/main/adoc/modules/applib-svc/pages/CommandContext.adoc
index a4fbcf1..5170943 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/pages/CommandContext.adoc
+++ b/api/applib/src/main/adoc/modules/applib-svc/pages/CommandContext.adoc
@@ -4,6 +4,7 @@
 :page-partial:
 
 
+CAUTION: TODO v2 - to update, has been removed so that instead Interaction holds the Command, which in turn is accessed from InteractionContext.
 
 The `CommandContext` service is a xref:refguide:applib-ant:IsisSessionScope.adoc[request-scoped] service that reifies the invocation of an action on a domain object into an object itself.
 This reified information is encapsulated within the xref:CommandContext.adoc#command[`Command`] object.
@@ -28,17 +29,6 @@ This is normally done using the `CommandWithDto` subtype, that can return a repr
 
 
 
-//== Screencast
-//
-//
-//The link:https://www.youtube.com/watch?v=tqXUZkPB3EI[screencast] provides a run-through of the command (profiling) service, auditing service, publishing service (note: auditing service has since been replaced by `AuditerService`, and publishing service by `PublisherService`).
-//It also shows how commands can be run in the background either explicitly by scheduling through the background service or implicitly by way of a framework annotation.
-//
-//
-//[NOTE]
-//====
-//Note that this screencast shows an earlier version of the xref:vw:ROOT:about.adoc[Wicket viewer] UI (specifically, pre 1.8.0).
-//====
 
 
 
@@ -63,7 +53,12 @@ include::refguide:applib-svc:example$services/command/Command.java[tags="refguid
 
 <.> user that initiated the action.
 <.> date/time at which this action was created.
-<.> bookmark of the target object (entity or service) on which this action was performed <.> holds a string representation of the invoked action <.> human-friendly description of the class of the target object <.> human-friendly name of the action invoked on the target object <.> human-friendly description of the arguments with which the action was invoked <.> formal (XML or similar) specification of the action to invoke/being invoked <.> whether this command is executed in the foregroun [...]
+<.> bookmark of the target object (entity or service) on which this action was performed
+<.> holds a string representation of the invoked action
+<.> human-friendly description of the class of the target object
+<.> human-friendly name of the action invoked on the target object
+<.> human-friendly description of the arguments with which the action was invoked <.> formal (XML or similar) specification of the action to invoke/being invoked
+<.> whether this command is executed in the foreground or background
 +
 [source,java,indent=0]
 ----
@@ -110,23 +105,6 @@ public class ToDoItem ... {
 As an alternative to annotating every action with xref:refguide:applib-ant:Action.adoc#command[`@Action#command()`], use the xref:refguide:config:sections/isis.applib.adoc#isis.applib.annotation.action.command[`isis.applib.annotation.action.command`] to define a global default.
 
 
-//The xref:refguide:applib-ant:Action.adoc#command[`@Action#command()`] annotation can also be used to specify whether the command should be performed in the background, for example:
-//
-//[source,java]
-//----
-//public class ToDoItem ... {
-//    @Action(commandExecuteIn=CommandExecuteIn.BACKGROUND)
-//    public ToDoItem scheduleImplicitly() {
-//        completeSlowly(3000);
-//        return this;
-//    }
-//}
-//----
-//
-//When a background command is invoked, the user is returned the command object itself (to provide a handle to the command being invoked).
-//
-//This requires that an implementation of xref:refguide:applib-svc:CommandService.adoc[`CommandService`] that persists the commands (such as the (non-ASF) link:https://platform.incode.org[Incode Platform^]'s command module's `CommandService`) is configured.
-//It also requires that a scheduler is configured to execute the background commands, see xref:refguide:applib-svc:_BackgroundCommandService.adoc[`BackgroundCommandService`]).
 
 
 
@@ -186,14 +164,10 @@ public class ToDoItem ... {
 The implementation of xref:refguide:applib-svc:CommandContext.adoc#command[`Command`] is provided by the xref:refguide:applib-svc:CommandService.adoc[`CommandService`].
 The default implementation of that service creates an in-memory instance, but other implementations might choose to persist the `Command`, eg so its execution can be deferred, or replayed.
 
-//Implementations of `CommandService` and `BackgroundCommandService` are intended to go together, so that child ``Command``s persistent (to be executed in the background) can be associated with their parent ``Command``s (executed in the foreground, with the background `Command` created explicitly through the xref:refguide:applib-svc:BackgroundService.adoc[`BackgroundService`]).
 
 The xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`] service itself is very similar in nature to the xref:refguide:applib-svc:InteractionContext.adoc[`InteractionContext`], in that the `Command` object accessed through it is very similar to the xref:refguide:applib-svc:InteractionContext.adoc#interaction[`Interaction`] object obtained from the `InteractionContext`.
 The principle distinction is that while `Command` represents the __intention__ to invoke an action or edit a property, the `Interaction` (and contained ``Execution``s) represents the actual execution.
 
-//Most of the time a `Command` will be followed directly by its corresponding `Interaction`.
-//However, if the `Command` is annotated to run in the background (using xref:refguide:applib-ant:Action.adoc#command[`@Action#commandExecuteIn()`], or is explicitly created through the xref:refguide:applib-svc:BackgroundService.adoc[`BackgroundService`], then the actual interaction/execution is deferred until some other mechanism invokes the command (eg as described xref:userguide:btb:about.adoc#BackgroundCommandExecution[here]).
-//The persistence of background commands requires a configured xref:refguide:applib-svc:_BackgroundCommandService.adoc[`BackgroundCommandService`]) to actually persist such commands for execution.
 
 
 ``Command``s are often combined with implementations of the xref:refguide:applib-svc:PublisherService.adoc[`PublisherService`] and sometimes also the xref:refguide:applib-svc:AuditerService.adoc[`AuditerService`]:
diff --git a/api/applib/src/main/adoc/modules/applib-svc/pages/CommandService.adoc b/api/applib/src/main/adoc/modules/applib-svc/pages/CommandService.adoc
index 2b8d4bf..2f97605 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/pages/CommandService.adoc
+++ b/api/applib/src/main/adoc/modules/applib-svc/pages/CommandService.adoc
@@ -4,9 +4,9 @@
 :page-partial:
 
 
-The `CommandService` service supports the xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`] service such that xref:refguide:applib-svc:CommandContext.adoc#command[`Command`] objects (that reify the invocation of an action/edit of a property on a domain object) can be persisted.
+CAUTION: TODO - v2 - to update, has been simplified.
 
-//The primary use case for persistent ``Command``s is in support of background commands; they act as a parent to any background commands that can be persisted either explicitly using the xref:refguide:applib-svc:BackgroundService.adoc[`BackgroundService`], or implicitly by way of the xref:refguide:applib-ant:Action.adoc#command[`@Action#command()`] annotation.
+The `CommandService` service supports the xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`] service such that xref:refguide:applib-svc:CommandContext.adoc#command[`Command`] objects (that reify the invocation of an action/edit of a property on a domain object) can be persisted.
 
 ``Command``s
 //also
@@ -27,7 +27,7 @@ The `CommandService` service defines the following very simple API:
 
 [source,java]
 ----
-include::refguide:applib-svc:example$services/command/spi/CommandService.java[tags="refguide"]
+include::refguide:applib-svc:example$services/command/CommandService.java[tags="refguide"]
 ----
 
 <.> Instantiate the appropriate instance of the xref:refguide:applib-svc:CommandContext.adoc#command[`Command`] (as defined by the
@@ -44,9 +44,6 @@ The xref:extensions:command-log:about.adoc[Command Log] extension provides an im
 == Related Services
 
 As discussed above, this service supports the xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`], providing the ability for `Command` objects to be persisted.
-//This is closely related to the xref:refguide:applib-svc:_BackgroundCommandService.adoc[`BackgroundCommandService`]that allows the xref:refguide:applib-svc:BackgroundService.adoc[`BackgroundService`] to schedule commands for background/asynchronous execution.
-
-//The implementations of `CommandService` and `BackgroundCommandService` are intended to go together, so that persistent parent `Command`s can be associated with their child background `Command`s.
 
 The services provided by this module combines very well with the xref:refguide:applib-svc:AuditerService.adoc[`AuditerService`].
 The `CommandService` captures the __cause__ of an interaction (an action was invoked, a property was edited), while the `AuditerService` captures the __effect__ of that interaction in terms of changed state.
diff --git a/api/applib/src/main/adoc/modules/applib-svc/pages/InteractionContext.adoc b/api/applib/src/main/adoc/modules/applib-svc/pages/InteractionContext.adoc
index 4ceccde..c901741 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/pages/InteractionContext.adoc
+++ b/api/applib/src/main/adoc/modules/applib-svc/pages/InteractionContext.adoc
@@ -155,10 +155,12 @@ The services are used within the framework however, primarily to support the
 xref:refguide:applib-svc:PublisherService.adoc[`PublisherService`] SPI, and to emit domain events over the
 xref:refguide:applib-svc:EventBusService.adoc[`EventBusService`].
 
-== Related Classes
-
-This service is very similar in nature to xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`], in that the `Interaction` object accessed through it is very similar to the `Command` object obtained from the `CommandContext`.
-The principle distinction is that while `Command` represents the __intention__ to invoke an action or edit a property, the `Interaction` (and contained ``Execution``s) represents the actual execution.
+//== Related Classes
+//
+// TODO: CommandContext removed, instead just InteractionContext
+//
+//This service is very similar in nature to xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`], in that the `Interaction` object accessed through it is very similar to the `Command` object obtained from the `CommandContext`.
+//The principle distinction is that while `Command` represents the __intention__ to invoke an action or edit a property, the `Interaction` (and contained ``Execution``s) represents the actual execution.
 
 //Most of the time a `Command` will be followed directly by its corresponding `Interaction`.
 //However, if the `Command` is annotated to run in the background (using xref:refguide:applib-ant:Action.adoc#command[`@Action#commandExecuteIn()`], or is explicitly created through the xref:refguide:applib-svc:BackgroundService.adoc[`BackgroundService`], then the actual interaction/execution is deferred until some other mechanism invokes the command (eg as described xref:userguide:btb:about.adoc#BackgroundCommandExecution[here]).
diff --git a/api/applib/src/main/adoc/modules/applib-svc/pages/_BackgroundCommandService.adoc b/api/applib/src/main/adoc/modules/applib-svc/pages/_BackgroundCommandService.adoc
deleted file mode 100644
index 62f50c8..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/pages/_BackgroundCommandService.adoc
+++ /dev/null
@@ -1,107 +0,0 @@
-= `BackgroundCommandService2`
-
-:Notice: 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 ag [...]
-:page-partial:
-
-WARNING: TODO: v2 - temporarily removed, to be reinstated in v2.0
-
-The `BackgroundCommandService2` (SPI) service supports the xref:refguide:applib-svc:BackgroundService.adoc[`BackgroundService`] (API) service, persisting action invocations as commands such that they can subsequently be invoked in the background.
-
-The `BackgroundService` is responsible for capturing a memento representing the action invocation, and then hands off to the xref:refguide:applib-svc:BackgroundCommandService.adoc[`BackgroundCommandService`] `BackgroundCommandService` to actually persist it.
-
-The persisting of commands is only half the story; there needs to be a separate process to read the commands and execute them.
-The abstract xref:refguide:applib-svc:BackgroundCommandExecution.adoc[`BackgroundCommandExecution`] provides a mechanism to execute such commands.  This can be considered an API, albeit "internal" because the implementation relies on internals of the framework.
-
-
-
-
-== SPI
-
-The SPI of the `BackgroundCommandService2` is:
-
-[source,java]
-----
-public interface BackgroundCommandService2 {
-    void schedule(
-            CommandDto dto,             // <1>
-            Command parentCommand,      // <2>
-            String targetClassName,
-            String targetActionName,
-            String targetArgs);
-
-}
-----
-<1> an instance of a xref:refguide:schema:cmd.adoc[`CommandDto`] capturing the details of the action invocation or property edit to be retained (eg persisted to a database) so that it can be executed at a later time
-<2> reference to the parent `Command` requesting the action be performed as a background command.  This allows information such as the initiating user to be obtained.
-
-
-
-== "Internal" SPI
-
-The `BackgroundCommandExecution` (in isis-core) is an abstract template class for  xref:userguide:btb:about.adoc#AbstractIsisSessionTemplate[headless access], that defines an abstract hook method to obtain background `Command`s to be executed:
-
-[source,java]
-----
-public abstract class BackgroundCommandExecution
-                         extends AbstractIsisSessionTemplate {
-    ...
-    protected abstract List<? extends Command> findBackgroundCommandsToExecute();
-    ...
-}
-----
-
-The developer is required to implement this hook method in a subclass.
-
-
-
-
-== Implementation
-
-The (non-ASF) link:https://platform.incode.org[Incode Platform^]'s command module provides an implementation (`org.isisaddons.module.command.dom.BackgroundCommandServiceJdo`) that persists ``Command``s using the JDO/DataNucleus object store.
-It further provides a number of supporting services:
-
-* `org.isisaddons.module.command.dom.BackgroundCommandServiceJdoRepository` is a repository to search for persisted background ``Command``s
-
-* `org.isisaddons.module.command.dom.BackgroundCommandServiceJdoContributions` contributes actions for searching for persisted child and sibling ``Command``s.
-
-The module also provides a concrete subclass of `BackgroundCommandExecution` that knows how to query for persisted (background) `Command`s such that they can be executed by a scheduler.
-
-[TIP]
-====
-Details of setting up the Quartz scheduler to actually execute these persisted commands can be found on the xref:refguide:applib-svc:BackgroundService.adoc[`BackgroundService`] page.
-====
-
-
-
-
-== Usage
-
-Background commands can be created either declaratively or imperatively.
-
-The declarative approach involves annotating an action using xref:refguide:applib-ant:Action.adoc#command[`@Action#command()`] with `@Action#commandExecuteIn=CommandExecuteIn.BACKGROUND`.
-
-The imperative approach involves explicitly calling the xref:refguide:applib-svc:BackgroundService.adoc[`BackgroundService`] from within domain object's action.
-
-
-
-
-== Alternative Implementations
-
-The (non-ASF) link:https://platform.incode.org[Incode Platform^]'s command module provides an implementation of this service (`BackgroundCommandService`), and also provides a number of related domain services (`BackgroundCommandServiceJdo`, `BackgroundCommandJdoRepository` and `BackgroundCommandServiceJdoContributions`).
-This module also provides service implementations of the xref:refguide:applib-svc:CommandService.adoc[`CommandService`].
-
-If contributions are not required in the UI, these can be suppressed either using security or by implementing a xref:userguide:btb:about.adoc#vetoing-visibility[vetoing subscriber].
-
-
-
-
-== Related Services
-
-As discussed above, this service supports the xref:refguide:applib-svc:BackgroundService.adoc[`BackgroundService`] , persisting `Command`s such that they can be executed in the background.
-
-There is also a tie-up with the xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`] and its supporting xref:refguide:applib-svc:CommandService.adoc[`CommandService`] domain service. The `CommandContext` service is responsible for providing a parent `Command` with which the background `Command`s can then be associated as children, while the `CommandService` is responsible for persisting those parent `Command`s (analogous to the way in which the `BackgroundCommandService` persis [...]
-
-What that means is that the implementations of `CommandService` and `BackgroundCommandService` go together, hence both implemented in the (non-ASF) link:https://platform.incode.org[Incode Platform^]'s  command module.).
-
-
-
diff --git a/api/applib/src/main/adoc/modules/applib-svc/pages/_BackgroundService.adoc b/api/applib/src/main/adoc/modules/applib-svc/pages/_BackgroundService.adoc
deleted file mode 100644
index 6642f4b..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/pages/_BackgroundService.adoc
+++ /dev/null
@@ -1,120 +0,0 @@
-= `BackgroundService`
-
-:Notice: 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 ag [...]
-:page-partial:
-
-WARNING: TODO: v2 - temporarily removed, will be reinstated for v2.0
-
-The `BackgroundService` domain service (and its various supertypes), and also the companion xref:refguide:applib-svc:BackgroundCommandService.adoc[`BackgroundCommandService2`] SPI service, enable commands to be persisted such that they may be invoked in the background.
-
-The `BackgroundService` is responsible for capturing a memento representing the command in a typesafe way, and persisting it rather than executing it directly.
-
-The default `BackgroundServiceDefault` implementation works by using a proxy wrapper around the target so that it can capture the action to invoke and its arguments.
-
-This is done using xref:core:runtime-services:CommandDtoServiceInternal.adoc[`CommandDtoServiceInternal`].
-
-The persistence delegates the persistence of the memento to an appropriate implementation of the companion `BackgroundCommandService2`.
-One such implementation of `BackgroundCommandService` is provided by (non-ASF) http://github.com/incodehq/incode-platform[Incode Platform's command] module.
-
-The persisting of commands is only half the story; there needs to be a separate process to read the commands and execute them.
-The `BackgroundCommandExecution` abstract class (discussed xref:refguide:applib-svc:BackgroundCommandExecution.adoc[below]) provides infrastructure to do this; the concrete implementation of this class depends on the configured `BackgroundCommandService` (in order to query for the persisted (background) ``Command``s.
-
-
-
-== API & Implementation
-
-The API is:
-
-[source,java]
-----
-public interface BackgroundService {
-    <T> T execute(final T object);                              // <1>
-    <T> T executeMixin(Class<T> mixinClass, Object mixedIn);    // <2>
-}
-----
-<1> returns a proxy around the domain object; any methods executed against this proxy will result in a command (to invoke the corresponding action) being persisted by xref:refguide:applib-svc:BackgroundCommandService2.adoc[`BackgroundCommandService2`]
-<2> Returns a proxy around the mixin; any methods executed against this proxy will result in a command (to invoke the corresponding mixin action) being persisted by xref:refguide:applib-svc:BackgroundCommandService2.adoc[`BackgroundCommandService2`].
-
-The default implementation is provided by core (`o.a.i.core.runtime.services.background.BackgroundServiceDefault`).
-
-To provide an alternative implementation, subclass and link:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/annotation/Order.html[`@Order`] or equivalent (as explained in the xref:refguide:applib-svc:about.adoc#overriding-the-services[introduction] to this guide).
-
-
-== Usage
-
-Using the service is very straight-forward; wrap the target domain object using `BackgroundService#execute(...)` and invoke the method on the object returned by that method.
-
-For example:
-
-[source,java]
-----
-public void submitCustomerInvoices() {
-    for(Customer customer: customerRepository.findCustomersToInvoice()) {
-        backgroundService.execute(customer).submitInvoice();
-    }
-    messageService.informUser("Calculating...");
-}
-----
-
-This will create a bunch of background commands executing the `submitInvoice()` action for each of the customers returned from the customer repository.
-
-The action method invoked must be part of the Apache Isis metamodel, which is to say it must be public, accept only scalar arguments, and must not be annotated with xref:refguide:applib-ant:Programmatic.adoc[`@Programmatic`] or `@Ignore`.
-However, it may be annotated with xref:refguide:applib-ant:Action.adoc#hidden[`@Action#hidden()`] or xref:refguide:applib-ant:ActionLayout.adoc#hidden[`@ActionLayout#hidden()`] and it will still be invoked.
-
-In fact, when invoked by the background service, no business rules (hidden, disabled, validation) are enforced; the action method must take responsibility for performing appropriate validation and error checking.
-
-[TIP]
-====
-If you want to check business rules, you can use xref:refguide:applib-ant:WrapperFactory.adoc[`@WrapperFactory#wrapNoExecute(...)`].
-====
-
-
-
-== End-user experience
-
-For the end-user, executing an action that delegates work off to the `BackgroundService` raises the problem of how does the user know the work is complete?
-
-One option is for the background jobs to take responsibility to notify the user themselves.
-In the above example, this would be the `submitInvoice()` method called upon each customer.
-One could imagine more complex designs where only the final command executed notifies the user.
-
-However, an alternative is to rely on the fact that the `BackgroundService` will automatically hint that the `Command` representing the original interaction (to `submitCustomerInvoices()` in the example above) should be persisted.
-This will be available if the related xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`] and xref:refguide:applib-svc:CommandService.adoc[`CommandService`] domain services are configured, and the `CommandService` supports persistent commands.
-Note that (non-ASF) link:https://platform.incode.org[Incode Platform^]'s command module does indeed provide such an implementation of `CommandService` (as well as of the required `BackgroundCommandService`).
-
-Thus, the original action can run a query to obtain it corresponding `Command`, and return this to the user.
-The upshot is that the child ``Command``s created by the `BackgroundService` will then be associated with `Command` for the original action.
-
-We could if we wanted write the above example as follows:
-
-[source,java]
-----
-public Command submitCustomerInvoices() {
-    for(Customer customer: customerRepository.findCustomersToInvoice()) {
-        backgroundService.execute(customer).submitInvoice();
-    }
-    return commandContext.getCommand();
-}
-@Inject
-CommandContext commandContext;  // <1>
-----
-<1> the injected xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`] domain service.
-
-The user would be returned a domain object representing their action invocation.
-
-
-
-
-== Related Services
-
-This service is closely related to the xref:refguide:applib-svc:CommandContext.adoc[`CommandContext`] and also that service's supporting xref:refguide:applib-svc:CommandService.adoc[`CommandService`] service.
-
-The `CommandContext` service is responsible for providing a parent `Command` with which the background ``Command``s can then be associated as children, while the `CommandService` is responsible for persisting those parent `Command`s.
-The latter is analogous to the way in which the `BackgroundCommandService` persists the child background `Command`s.
-
-The implementations of `CommandService` and `BackgroundCommandService` go together; typically both parent `Command`s and child background `Command`s will be persisted in the same way.
-The (non-ASF) link:https://platform.incode.org[Incode Platform^]'s command module provides implementations of both (see xref:refguide:applib-svc:CommandService.adoc[`CommandService`] and xref:refguide:applib-svc:BackgroundCommandService.adoc[`BackgroundCommandService`]).
-
-The xref:core:runtime-services:CommandDtoServiceInternal.adoc[`CommandDtoServiceInternal`] is used to obtain a memento of the command such that it can be persisted.
-
-
diff --git a/api/applib/src/main/adoc/modules/applib-svc/pages/_BackgroundService/_Quartz.adoc b/api/applib/src/main/adoc/modules/applib-svc/pages/_BackgroundService/_Quartz.adoc
deleted file mode 100644
index 14d1859..0000000
--- a/api/applib/src/main/adoc/modules/applib-svc/pages/_BackgroundService/_Quartz.adoc
+++ /dev/null
@@ -1,165 +0,0 @@
-[[Quartz]]
-= Quartz Scheduler Configuration
-
-:Notice: 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 ag [...]
-:page-partial:
-
-WARNING: TODO: v2 - background command execution temporarily removed, will be reinstated for v2.0
-
-The last part of the puzzle is to actually run the (appropriate implementation of) `BackgroundCommandExecution`).
-This could be run in a batch job overnight, or run continually by, say, the http://quartz-scheduler.org[Quartz] scheduler or by link:http://camel.apache.org[Apache Camel].
-This section looks at configuring Quartz.
-
-If using (non-ASF) link:https://platform.incode.org[Incode Platform^]'s command module, then note that this already provides a suitable concrete implementation, namely `org.isisaddons.module.command.dom.BackgroundCommandExecutionFromBackgroundCommandServiceJdo`.
-We therefore just need to schedule this to run as a Quartz job.
-
-
-First, we need to define a Quartz job, for example:
-
-[source,java]
-----
-import org.isisaddons.module.command.dom.BackgroundCommandExecutionFromBackgroundCommandServiceJdo;
-public class BackgroundCommandExecutionQuartzJob extends AbstractIsisQuartzJob {
-    public BackgroundCommandExecutionQuartzJob() {
-        super(new BackgroundCommandExecutionFromBackgroundCommandServiceJdo());
-    }
-}
-----
-
-where `AbstractIsisQuartzJob` is in turn the following boilerplate:
-
-[source,java]
-----
-package domainapp.webapp.quartz;
-import org.quartz.Job;
-import org.quartz.JobExecutionContext;
-import org.quartz.JobExecutionException;
-...
-public class AbstractIsisQuartzJob implements Job {
-    public static enum ConcurrentInstancesPolicy {
-        SINGLE_INSTANCE_ONLY,
-        MULTIPLE_INSTANCES
-    }
-
-    private final AbstractIsisSessionTemplate isisRunnable;
-    private final ConcurrentInstancesPolicy concurrentInstancesPolicy;
-    private boolean executing;
-
-    public AbstractIsisQuartzJob(AbstractIsisSessionTemplate isisRunnable) {
-        this(isisRunnable, ConcurrentInstancesPolicy.SINGLE_INSTANCE_ONLY);
-    }
-    public AbstractIsisQuartzJob(
-            AbstractIsisSessionTemplate isisRunnable,
-            ConcurrentInstancesPolicy concurrentInstancesPolicy) {
-        this.isisRunnable = isisRunnable;
-        this.concurrentInstancesPolicy = concurrentInstancesPolicy;
-    }
-
-    public void execute(final JobExecutionContext context)
-            throws JobExecutionException {
-        final AuthenticationSession authSession = newAuthSession(context);
-        try {
-            if(concurrentInstancesPolicy == ConcurrentInstancesPolicy.SINGLE_INSTANCE_ONLY &&
-               executing) {
-                return;
-            }
-            executing = true;
-
-            isisRunnable.execute(authSession, context);
-        } finally {
-            executing = false;
-        }
-    }
-
-    AuthenticationSession newAuthSession(JobExecutionContext context) {
-        String user = getKey(context, SchedulerConstants.USER_KEY);
-        String rolesStr = getKey(context, SchedulerConstants.ROLES_KEY);
-        String[] roles = Iterables.toArray(
-                Splitter.on(",").split(rolesStr), String.class);
-        return new SimpleSession(user, roles);
-    }
-
-    String getKey(JobExecutionContext context, String key) {
-        return context.getMergedJobDataMap().getString(key);
-    }
-}
-----
-
-
-This job can then be configured to run using Quartz' `quartz-config.xml` file:
-
-[source,xml]
-----
-<?xml version="1.0" encoding="UTF-8"?>
-<job-scheduling-data
-    xmlns="http://www.quartz-scheduler.org/xml/JobSchedulingData"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://www.quartz-scheduler.org/xml/JobSchedulingData
-http://www.quartz-scheduler.org/xml/job_scheduling_data_1_8.xsd"
-    version="1.8">
-    <schedule>
-       <job>
-         <name>BackgroundCommandExecutionJob</name>
-         <group>Isis</group>
-         <description>
-                Poll and execute any background actions persisted by the BackgroundActionServiceJdo domain service
-            </description>
-         <job-class>domainapp.webapp.quartz.BackgroundCommandExecutionQuartzJob</job-class>
-         <job-data-map>
-          <entry>
-              <key>webapp.scheduler.user</key>
-              <value>scheduler_user</value>
-          </entry>
-          <entry>
-              <key>webapp.scheduler.roles</key>
-              <value>admin_role</value>
-          </entry>
-         </job-data-map>
-       </job>
-       <trigger>
-         <cron>
-          <name>BackgroundCommandExecutionJobEveryTenSeconds</name>
-          <job-name>BackgroundCommandExecutionJob</job-name>
-          <job-group>Isis</job-group>
-          <cron-expression>0/10 * * * * ?</cron-expression>
-         </cron>
-       </trigger>
-    </schedule>
-</job-scheduling-data>
-----
-
-The remaining two pieces of configuration are the `quartz.properties` file:
-
-[source,ini]
-----
-org.quartz.scheduler.instanceName = SchedulerQuartzConfigXml
-org.quartz.threadPool.threadCount = 1
-org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
-org.quartz.plugin.jobInitializer.class =org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
-org.quartz.plugin.jobInitializer.fileNames = webapp/scheduler/quartz-config.xml
-org.quartz.plugin.jobInitializer.failOnFileNotFound = true
-----
-
-and the entry in `web.xml` for the Quartz servlet:
-
-[source,xml]
-----
-<servlet>
-     <servlet-name>QuartzInitializer</servlet-name>
-     <servlet-class>org.quartz.ee.servlet.QuartzInitializerServlet</servlet-class>
-     <init-param>
-         <param-name>config-file</param-name>
-         <param-value>webapp/scheduler/quartz.properties</param-value>
-     </init-param>
-     <init-param>
-         <param-name>shutdown-on-unload</param-name>
-         <param-value>true</param-value>
-     </init-param>
-     <init-param>
-         <param-name>start-scheduler-on-load</param-name>
-         <param-value>true</param-value>
-     </init-param>
-     <load-on-startup>1</load-on-startup>
- </servlet>
-----
-
diff --git a/api/applib/src/main/adoc/modules/applib-svc/partials/module-nav.adoc b/api/applib/src/main/adoc/modules/applib-svc/partials/module-nav.adoc
index 54691db..f95b4fc 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/partials/module-nav.adoc
+++ b/api/applib/src/main/adoc/modules/applib-svc/partials/module-nav.adoc
@@ -6,10 +6,6 @@
 ** xref:refguide:applib-svc:AcceptHeaderService.adoc[AcceptHeaderService]
 ** xref:refguide:applib-svc:ApplicationFeatureRepository.adoc[ApplicationFeatureRepository]
 ** xref:refguide:applib-svc:AuditerService.adoc[AuditerService]
-//** xref:refguide:applib-svc:_BackgroundCommandService.adoc[BackgroundCommandService]
-//** xref:refguide:applib-svc:_BackgroundService.adoc[BackgroundService]
-//*** xref:refguide:applib-svc:_BackgroundCommandExecution.adoc[BackgroundCommandExecution]
-//*** xref:refguide:applib-svc:_Quartz.adoc[Quartz]
 ** xref:refguide:applib-svc:BookmarkService.adoc[BookmarkService]
 ** xref:refguide:applib-svc:BookmarkUiService.adoc[BookmarkUiService]
 ** xref:refguide:applib-svc:ClockService.adoc[ClockService]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java b/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java
index 61dc1cb..ada8693 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/IsisModuleApplib.java
@@ -33,17 +33,18 @@ import org.apache.isis.applib.services.audit.AuditerServiceLogging;
 import org.apache.isis.applib.services.bookmark.BookmarkHolder_lookup;
 import org.apache.isis.applib.services.bookmark.BookmarkHolder_object;
 import org.apache.isis.applib.services.clock.ClockService;
-import org.apache.isis.applib.services.command.CommandContext;
+import org.apache.isis.applib.services.command.CommandService;
+import org.apache.isis.applib.services.command.spi.CommandServiceListener;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
+import org.apache.isis.applib.services.commanddto.processor.spi.CommandDtoProcessorServiceIdentity;
 import org.apache.isis.applib.services.confview.ConfigurationMenu;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandDto;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandsDto;
 import org.apache.isis.applib.services.iactn.InteractionContext;
 import org.apache.isis.applib.services.jaxb.JaxbServiceDefault;
 import org.apache.isis.applib.services.layout.LayoutServiceMenu;
 import org.apache.isis.applib.services.metamodel.MetaModelServiceMenu;
 import org.apache.isis.applib.services.publish.PublisherServiceLogging;
 import org.apache.isis.applib.services.session.SessionLoggingServiceLogging;
-import org.apache.isis.applib.services.wrapper.control.AsyncControlService;
 import org.apache.isis.schema.IsisModuleSchema;
 
 @Configuration
@@ -71,7 +72,9 @@ import org.apache.isis.schema.IsisModuleSchema;
         // @Service's
         AuditerServiceLogging.class,
         ClockService.class,
-        CommandContext.class,
+        CommandDtoProcessorServiceIdentity.class,
+        CommandService.class,
+        CommandServiceListener.Null.class,
         ContentMappingServiceForCommandDto.class,
         ContentMappingServiceForCommandsDto.class,
         InteractionContext.class,
@@ -79,9 +82,6 @@ import org.apache.isis.schema.IsisModuleSchema;
         PublisherServiceLogging.class,
         SessionLoggingServiceLogging.class,
 
-        // @Component's
-        AsyncControlService.class,
-
 })
 public class IsisModuleApplib {
 
diff --git a/api/applib/src/main/java/org/apache/isis/applib/annotation/Action.java b/api/applib/src/main/java/org/apache/isis/applib/annotation/Action.java
index c73d945..f8c0703 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/annotation/Action.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/annotation/Action.java
@@ -26,11 +26,10 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 import org.apache.isis.applib.events.domain.ActionDomainEvent;
-import org.apache.isis.applib.services.command.CommandDtoProcessor;
-import org.apache.isis.applib.services.command.CommandWithDto;
-import org.apache.isis.applib.services.command.spi.CommandService;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandDto;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandsDto;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
+import org.apache.isis.applib.services.command.CommandService;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
 import org.apache.isis.applib.value.Blob;
 import org.apache.isis.applib.value.Clob;
 
@@ -99,29 +98,6 @@ public @interface Action {
 
     // end::refguide[]
     /**
-     * How the {@link org.apache.isis.applib.services.command.Command Command} object provided by the
-     * {@link org.apache.isis.applib.services.command.CommandContext CommandContext} domain service should be persisted.
-     */
-    // tag::refguide[]
-    CommandPersistence commandPersistence()                         // <.>
-            default CommandPersistence.PERSISTED;
-
-    // end::refguide[]
-    /**
-     * How the command/action should be executed.
-     *
-     * <p>
-     * If the corresponding {@link org.apache.isis.applib.services.command.Command Command} object is persisted,
-     * then its {@link org.apache.isis.applib.services.command.Command#getExecuteIn() invocationType} property
-     * will be set to this value.
-     * </p>
-     */
-    // tag::refguide[]
-    CommandExecuteIn commandExecuteIn()                             // <.>
-            default CommandExecuteIn.FOREGROUND;
-
-    // end::refguide[]
-    /**
      * The {@link CommandDtoProcessor} to process this command's DTO.
      *
      * <p>
diff --git a/api/applib/src/main/java/org/apache/isis/applib/annotation/CommandExecuteIn.java b/api/applib/src/main/java/org/apache/isis/applib/annotation/CommandExecuteIn.java
deleted file mode 100644
index 14afaed..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/annotation/CommandExecuteIn.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.annotation;
-
-/**
- * Whether a command should be executed immediately and synchronously in the foreground or rather should only be
- * persisted (such that it can be executed asynchronously in the background by some other mechanism).
- *
- * <p>
- *     Note: this enum is <i>not</i> an inner class of the {@link org.apache.isis.applib.annotation.Action} annotation
- *     because in the future we may also support commands for {@link org.apache.isis.applib.annotation.Property} and
- *     {@link org.apache.isis.applib.annotation.Collection}.
- * </p>
- */
-// tag::refguide[]
-public enum CommandExecuteIn {
-    // end::refguide[]
-    /**
-     * Execute synchronously in the &quot;foreground&quot;, wait for the results.
-     */
-    // tag::refguide[]
-    FOREGROUND,
-    // end::refguide[]
-    /**
-     * Execute &quot;asynchronously&quot; through the {@link org.apache.isis.applib.services.background.BackgroundCommandService}, returning (if possible) the
-     * persisted {@link org.apache.isis.applib.services.command.Command command} object as a placeholder to the
-     * result.
-     */
-    // tag::refguide[]
-    BACKGROUND,
-    // end::refguide[]
-    /**
-     * For commands that are replicated from a master onto a slave and are to be replayed (typically using the same
-     * mechanism as "regular" background commands, eg a background job).
-     *
-     * <p>
-     *     For framework use, not intended to be used in application code.
-     * </p>
-     */
-    // tag::refguide[]
-    REPLAYABLE,
-    // end::refguide[]
-    /**
-     * For commands that have been excluded and will not run.
-     * These are typically for a replayable command that has hit an exception (which normally would prevent any further
-     * replayable commands from being replayed) and which the administrator has decided to skip.
-     */
-    // tag::refguide[]
-    EXCLUDED
-    // end::refguide[]
-    ;
-
-    public boolean isForeground() { return this == FOREGROUND; }
-    public boolean isBackground() { return this == BACKGROUND; }
-    public boolean isReplayable() { return this == REPLAYABLE; }
-    public boolean isExcluded() { return this == EXCLUDED; }
-
-    public static class Type {
-        private Type() {}
-        public static class Meta {
-            public static final int MAX_LEN = 10;
-            private Meta() {}
-        }
-    }
-    // tag::refguide[]
-}
-// end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/annotation/CommandPersistence.java b/api/applib/src/main/java/org/apache/isis/applib/annotation/CommandPersistence.java
deleted file mode 100644
index 7f7000a..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/annotation/CommandPersistence.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.annotation;
-
-/**
- * Whether the command should be persisted.
- */
-// tag::refguide[]
-public enum CommandPersistence {
-
-    // end::refguide[]
-    /**
-     * (If the configured {@link org.apache.isis.applib.services.command.spi.CommandService} supports it), indicates that the
-     * {@link org.apache.isis.applib.services.command.Command Command} object should be persisted.
-     */
-    // tag::refguide[]
-    PERSISTED,
-    // end::refguide[]
-    /**
-     * (If the configured {@link org.apache.isis.applib.services.command.spi.CommandService} supports it), indicates that the
-     * {@link org.apache.isis.applib.services.command.Command Command} object should only be persisted if
-     * another service, such as the {@link org.apache.isis.applib.services.background.BackgroundCommandService}, hints that it should.
-     */
-    // tag::refguide[]
-    IF_HINTED,
-    // end::refguide[]
-    /**
-     * (Even if the configured {@link org.apache.isis.applib.services.command.spi.CommandService} supports it), indicates that the
-     * {@link org.apache.isis.applib.services.command.Command Command} object should <i>not</i> be persisted (even if
-     * another service, such as the {@link org.apache.isis.applib.services.background.BackgroundCommandService}, hints that it should).
-     */
-    // tag::refguide[]
-    NOT_PERSISTED
-
-}
-// end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/annotation/Property.java b/api/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
index ac40f2d..65db374 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/annotation/Property.java
@@ -26,11 +26,9 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 import org.apache.isis.applib.events.domain.PropertyDomainEvent;
-import org.apache.isis.applib.services.command.CommandDtoProcessor;
-import org.apache.isis.applib.services.command.CommandWithDto;
-import org.apache.isis.applib.services.command.spi.CommandService;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandDto;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandsDto;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandsDto;
 import org.apache.isis.applib.spec.Specification;
 import org.apache.isis.applib.value.Blob;
 import org.apache.isis.applib.value.Clob;
@@ -52,7 +50,8 @@ public @interface Property {
 
     // end::refguide[]
     /**
-     * Whether the property edit should be reified into a {@link org.apache.isis.applib.services.command.Command} object.
+     * Whether the property edit should be reified into a
+     * {@link org.apache.isis.applib.services.command.Command} object.
      */
     // tag::refguide[]
     CommandReification command()                                // <.>
@@ -60,39 +59,10 @@ public @interface Property {
 
     // end::refguide[]
     /**
-     * How the {@link org.apache.isis.applib.services.command.Command Command} object provided by the
-     * {@link org.apache.isis.applib.services.command.CommandContext CommandContext} domain service should be persisted.
-     */
-    // tag::refguide[]
-    CommandPersistence commandPersistence()                     // <.>
-            default CommandPersistence.PERSISTED;
-
-    // end::refguide[]
-    /**
-     * How the command/property edit should be executed.
-     *
-     * <p>
-     * If the corresponding {@link org.apache.isis.applib.services.command.Command Command} object is persisted,
-     * then its {@link org.apache.isis.applib.services.command.Command#getExecuteIn() invocationType} property
-     * will be set to this value.
-     * </p>
-     */
-    // tag::refguide[]
-    CommandExecuteIn commandExecuteIn()                         // <.>
-            default CommandExecuteIn.FOREGROUND;
-
-    // end::refguide[]
-    /**
      * The {@link CommandDtoProcessor} to process this command's DTO.
      *
      * <p>
-     *     Specifying a processor requires that the implementation of {@link CommandService} provides a
-     *     custom implementation of {@link org.apache.isis.applib.services.command.Command} that additional extends
-     *     from {@link CommandWithDto}.
-     * </p>
-     *
-     * <p>
-     *     Tprocessor itself is used by {@link ContentMappingServiceForCommandDto} and
+     *     The processor itself is used by {@link ContentMappingServiceForCommandDto} and
      *     {@link ContentMappingServiceForCommandsDto} to dynamically transform the DTOs.
      * </p>
      */
@@ -210,7 +180,7 @@ public @interface Property {
      *
      * <p>
      *     For properties the default value, {@link org.apache.isis.applib.annotation.Optionality#DEFAULT}, usually
-     *     means that the property is required unless it has been overridden by {@link javax.jdo.annotations.Column}
+     *     means that the property is required unless it has been overridden by <code>javax.jdo.annotations.Column</code>
      *     with its <code>javax.jdo.annotations.Column#allowsNull()</code> attribute set to true.
      * </p>
      */
diff --git a/api/applib/src/main/java/org/apache/isis/applib/events/domain/AbstractDomainEvent.java b/api/applib/src/main/java/org/apache/isis/applib/events/domain/AbstractDomainEvent.java
index 2ddd967..0709768 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/events/domain/AbstractDomainEvent.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/events/domain/AbstractDomainEvent.java
@@ -22,9 +22,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.isis.applib.Identifier;
-import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.events.EventObjectBase;
-import org.apache.isis.applib.services.command.CommandContext;
 import org.apache.isis.applib.services.i18n.TranslatableString;
 import org.apache.isis.applib.util.ObjectContracts;
 import org.apache.isis.applib.util.ToString;
@@ -108,7 +106,7 @@ public abstract class AbstractDomainEvent<S> extends EventObjectBase<S> {
 
         /**
          * When the {@link org.apache.isis.applib.services.command.Command} is made available on the
-         * {@link org.apache.isis.applib.events.domain.ActionDomainEvent} via {@link CommandContext#getCommand()}.
+         * {@link org.apache.isis.applib.events.domain.ActionDomainEvent} via {@link org.apache.isis.applib.services.iactn.Interaction#getCommand()}.
          */
         public boolean isExecutingOrLater() {
             return isExecuting() || isExecuted();
diff --git a/api/applib/src/main/java/org/apache/isis/applib/mixins/layout/Object_rebuildMetamodel.java b/api/applib/src/main/java/org/apache/isis/applib/mixins/layout/Object_rebuildMetamodel.java
index 506c7a7..c68f875 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/mixins/layout/Object_rebuildMetamodel.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/mixins/layout/Object_rebuildMetamodel.java
@@ -22,7 +22,6 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.annotation.Contributed;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.Mixin;
@@ -33,7 +32,15 @@ import org.apache.isis.applib.services.metamodel.MetaModelService;
 
 import lombok.RequiredArgsConstructor;
 
-@Mixin(method="act") 
+@Action(
+        domainEvent = Object_rebuildMetamodel.ActionDomainEvent.class,
+        semantics = SemanticsOf.IDEMPOTENT,
+        restrictTo = RestrictTo.PROTOTYPING
+)
+@ActionLayout(
+        cssClassFa = "fa-sync",
+        position = ActionLayout.Position.PANEL
+)
 @RequiredArgsConstructor
 public class Object_rebuildMetamodel {
 
@@ -42,17 +49,6 @@ public class Object_rebuildMetamodel {
     public static class ActionDomainEvent
     extends org.apache.isis.applib.IsisModuleApplib.ActionDomainEvent<Object_rebuildMetamodel> {}
 
-    @Action(
-            domainEvent = ActionDomainEvent.class,
-            semantics = SemanticsOf.IDEMPOTENT,
-            commandPersistence = CommandPersistence.NOT_PERSISTED,
-            restrictTo = RestrictTo.PROTOTYPING
-            )
-    @ActionLayout(
-            contributed = Contributed.AS_ACTION,
-            cssClassFa = "fa-sync",
-            position = ActionLayout.Position.PANEL
-            )
     @MemberOrder(name = MixinConstants.METADATA_LAYOUT_GROUPNAME, sequence = "800.1")
     public Object act() {
         metaModelService.rebuild(holder.getClass());
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/DomainChangeRecord.java b/api/applib/src/main/java/org/apache/isis/applib/services/DomainChangeRecord.java
new file mode 100644
index 0000000..7cfc48d
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/DomainChangeRecord.java
@@ -0,0 +1,155 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.applib.services;
+
+import java.sql.Timestamp;
+import java.util.UUID;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberOrder;
+import org.apache.isis.applib.annotation.Optionality;
+import org.apache.isis.applib.annotation.Programmatic;
+import org.apache.isis.applib.annotation.Property;
+import org.apache.isis.applib.annotation.PropertyLayout;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.annotation.Where;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.message.MessageService;
+import org.apache.isis.applib.services.metamodel.BeanSort;
+import org.apache.isis.applib.services.metamodel.MetaModelService;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
+
+
+/**
+ * An abstraction of some sort of recorded change to a domain object: commands, audit entries or published events.
+ */
+public interface DomainChangeRecord extends HasUniqueId, HasUsername {
+
+    enum ChangeType {
+        COMMAND,
+        AUDIT_ENTRY,
+        PUBLISHED_INTERACTION;
+        @Override
+        public String toString() {
+            return name().replace("_", " ");
+        }
+    }
+
+    /**
+     * Distinguishes commands from audit entries from published events/interactions (when these are shown mixed together in a (standalone) table).
+     */
+    @Property
+    @PropertyLayout(hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
+    @MemberOrder(name="Identifiers", sequence = "1")
+    ChangeType getType();
+
+
+    /**
+     * The unique identifier (a GUID) of the transaction in which this change occurred.
+     */
+    @Property
+    @MemberOrder(name="Identifiers",sequence = "50")
+    UUID getUniqueId();
+
+
+    /**
+     * The user that caused the change.
+     */
+    @Property
+    @MemberOrder(name="Identifiers", sequence = "10")
+    String getUsername();
+
+
+    /**
+     * The time that the change occurred.
+     */
+    @Property
+    @MemberOrder(name="Identifiers", sequence = "20")
+    Timestamp getTimestamp();
+
+
+    /**
+     * The object type of the domain object being changed.
+     */
+    @Property
+    @PropertyLayout(named="Object Type")
+    @MemberOrder(name="Target", sequence = "10")
+    default String getTargetObjectType() {
+        return getTarget().getObjectType();
+    }
+
+
+
+    /**
+     * The {@link Bookmark} identifying the domain object that has changed.
+     */
+    @Property
+    @PropertyLayout(named="Object")
+    @MemberOrder(name="Target", sequence="30")
+    Bookmark getTarget();
+
+
+    /**
+     * The member interaction (ie action invocation or property edit) which caused the domain object to be changed.
+     *
+     * <p>
+     *     Populated for commands and for published events that represent action invocations or property edits.
+     * </p>
+     */
+    @Property(optionality = Optionality.OPTIONAL)
+    @PropertyLayout(named="Member", hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
+    @MemberOrder(name="Target", sequence = "20")
+    String getTargetMember();
+
+
+    /**
+     * The value of the property prior to it being changed.
+     *
+     * <p>
+     * Populated only for audit entries.
+     * </p>
+     */
+    @Property(optionality = Optionality.OPTIONAL)
+    @PropertyLayout(hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
+    @MemberOrder(name="Detail",sequence = "6")
+    String getPreValue();
+
+
+    /**
+     * The value of the property after it has changed.
+     *
+     * <p>
+     * Populated only for audit entries.
+     * </p>
+     */
+    @Property(optionality = Optionality.MANDATORY)
+    @PropertyLayout(hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
+    @MemberOrder(name="Detail",sequence = "7")
+    String getPostValue();
+
+
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/DomainChangeRecord_openTargetObject.java b/api/applib/src/main/java/org/apache/isis/applib/services/DomainChangeRecord_openTargetObject.java
new file mode 100644
index 0000000..7033bb5
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/DomainChangeRecord_openTargetObject.java
@@ -0,0 +1,61 @@
+package org.apache.isis.applib.services;
+
+import javax.inject.Inject;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.message.MessageService;
+import org.apache.isis.applib.services.metamodel.BeanSort;
+import org.apache.isis.applib.services.metamodel.MetaModelService;
+
+@Action(
+        semantics = SemanticsOf.SAFE
+        , associateWith = "target"
+        , associateWithSequence = "1"
+)
+@ActionLayout(named = "Open")
+public class DomainChangeRecord_openTargetObject {
+
+    private final DomainChangeRecord domainChangeRecord;
+    public DomainChangeRecord_openTargetObject(DomainChangeRecord domainChangeRecord) {
+        this.domainChangeRecord = domainChangeRecord;
+    }
+
+    @Action(semantics = SemanticsOf.SAFE, associateWith = "target", associateWithSequence = "1")
+    @ActionLayout(named = "Open")
+    public Object openTargetObject() {
+        try {
+            return bookmarkService != null
+                    ? bookmarkService.lookup(domainChangeRecord.getTarget())
+                    : null;
+        } catch(RuntimeException ex) {
+            if(ex.getClass().getName().contains("ObjectNotFoundException")) {
+                messageService.warnUser("Object not found - has it since been deleted?");
+                return null;
+            }
+            throw ex;
+        }
+    }
+
+    public boolean hideOpenTargetObject() {
+        return domainChangeRecord.getTarget() == null;
+    }
+
+    public String disableOpenTargetObject() {
+        final Object targetObject = domainChangeRecord.getTarget();
+        if (targetObject == null) {
+            return null;
+        }
+        final BeanSort sortOfObject = metaModelService.sortOf(domainChangeRecord.getTarget(), MetaModelService.Mode.RELAXED);
+        return !(sortOfObject.isViewModel() || sortOfObject.isEntity())
+                ? "Can only open view models or entities"
+                : null;
+    }
+
+    @Inject BookmarkService bookmarkService;
+    @Inject MessageService messageService;
+    @Inject MetaModelService metaModelService;
+
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/HasUsername.java b/api/applib/src/main/java/org/apache/isis/applib/services/HasUsername.java
index 7f7a240..4860b92 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/HasUsername.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/HasUsername.java
@@ -29,6 +29,10 @@ package org.apache.isis.applib.services;
 // tag::refguide[]
 public interface HasUsername {
 
+    /**
+     * The user that created this object.
+     * @return
+     */
     String getUsername();
 
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/TransactionScopeListener.java b/api/applib/src/main/java/org/apache/isis/applib/services/TransactionScopeListener.java
index 4c01d24..028bf40 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/TransactionScopeListener.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/TransactionScopeListener.java
@@ -19,11 +19,14 @@
 
 package org.apache.isis.applib.services;
 
+import org.apache.isis.applib.annotation.IsisInteractionScope;
+
 /**
- * Domain services that need to be aware of transaction boundaries can implement this interface.
+ * Domain services that need to be aware of transaction boundaries can
+ * implement this interface.
  * 
- * @apiNote Implementing services most likely need to be scoped in a way that binds the scope to 
- * the current thread (eg. {@link IsisInteractionScope}) 
+ * @apiNote Implementing services most likely need to be scoped in a way that
+ * binds the scope to the current thread (eg. {@link IsisInteractionScope})
  *  
  * @since 2.0 (renamed from WithTransactionScope)
  */
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundCommandService.java b/api/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundCommandService.java
deleted file mode 100644
index 4fcf317..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/services/background/BackgroundCommandService.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.background;
-
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-
-/**
- * Persists a {@link org.apache.isis.schema.cmd.v1.CommandDto command-reified} action such that it can be executed asynchronously,
- * for example through a Quartz scheduler.
- *
- * <p>
- * Separate from {@link BackgroundService} primarily so that the default
- * implementation, <tt>BackgroundServiceDefault</tt> (in <tt>isis-module-background</tt>) can
- * delegate to different implementations of this service.
- *
- * <p>
- * There is currently only implementation of this service, <tt>BackgroundCommandServiceJdo</tt> in
- * <tt>o.a.i.module:isis-module-command-jdo</tt>.  That implementation has no UI and no side-effects (the programmatic
- * API is through {@link org.apache.isis.applib.services.background.BackgroundService}).  It is therefore
- * annotated with {@link org.apache.isis.applib.annotation.DomainService} so that it is automatically registered as
- * a service.
- *
- */
-// tag::refguide[]
-public interface BackgroundCommandService extends AutoCloseable {
-
-    void schedule(
-            final CommandDto dto,
-            final Command parentCommand,
-            final String targetClassName,
-            final String targetActionName,
-            final String targetArgs);
-
-    /**
-     * @apiNote refined from AutoCloseable to not throw catched exceptions
-     */
-    @Override
-    default void close() {
-    }
-
-}
-// end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java b/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java
index 524fc48..0c4e17b 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/bookmark/Bookmark.java
@@ -73,6 +73,7 @@ public class Bookmark implements Serializable {
 
     /**
      * Round-trip with {@link #toString()} representation.
+     * @return
      */
     // tag::refguide[]
     public static Optional<Bookmark> parse(@Nullable String str) {
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/clock/ClockService.java b/api/applib/src/main/java/org/apache/isis/applib/services/clock/ClockService.java
index 597a56b..4583a4c 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/clock/ClockService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/clock/ClockService.java
@@ -18,15 +18,11 @@
  */
 package org.apache.isis.applib.services.clock;
 
-import java.sql.Timestamp;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.OffsetDateTime;
 import java.util.TimeZone;
 
 import javax.inject.Named;
+import javax.xml.datatype.XMLGregorianCalendar;
 
-import org.joda.time.DateTime;
 import org.joda.time.DateTimeZone;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Primary;
@@ -35,6 +31,7 @@ import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.clock.Clock;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
 
 /**
  * This service allows an application to be decoupled from the system time.  The most common use case is in support of
@@ -66,6 +63,10 @@ public class ClockService {
         return Clock.getTimeAsJavaSqlTimestamp();
     }
 
+    public XMLGregorianCalendar nowAsXMLGregorianCalendar() {
+        return JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(nowAsJavaSqlTimestamp());
+    }
+
     public long nowAsMillis() {
         return Clock.getEpochMillis();
     }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/Command.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/Command.java
index f030cb6..dc55a40 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/Command.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/command/Command.java
@@ -19,217 +19,175 @@
 package org.apache.isis.applib.services.command;
 
 import java.sql.Timestamp;
+import java.util.UUID;
 
-import org.apache.isis.applib.Identifier;
-import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.events.domain.ActionDomainEvent;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
 import org.apache.isis.applib.services.HasUniqueId;
-import org.apache.isis.applib.services.background.BackgroundCommandService;
+import org.apache.isis.applib.services.HasUsername;
 import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.bookmark.BookmarkService;
+import org.apache.isis.applib.services.commanddto.HasCommandDto;
 import org.apache.isis.applib.services.iactn.Interaction;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.applib.services.wrapper.control.AsyncControl;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
+import lombok.Getter;
+
 /**
  * Represents the <i>intention to</i> invoke either an action or modify a property.  There can be only one such
- * intention per (web) request, so a command is in effect request-scoped.  Note that {@link CommandContext} domain
- * service - from which the current {@link Command} can be obtained - is indeed annotated with
- * {@link javax.enterprise.context.RequestScoped @RequestScoped}.
+ * intention per (web) request, so a command is in effect interaction-scoped.
  *
  * <p>
- * Each Command can be reified into a {@link Command#getMemento() memento} by way of the (internal)
- * <tt>CommandDtoServiceInternal</tt> domain service; typically corresponding to the XML equivalent of a
- * {@link CommandDto} and conforming to the Apache Isis <a href="http://isis.apache.org/schema/cmd/">cmd</a> schema.
+ * Each Command holds a {@link CommandDto} (see Apache Isis <a href="http://isis.apache.org/schema/cmd/">cmd</a> schema)
+ * which reifies all the details in a serializable form.
  * </p>
  *
  * <p>
- *     The {@link Command} interface also captures details of the corresponding action invocation (or property edit),
+ *     It also captures details of the corresponding action invocation (or property edit),
  *     specifically when that action/edit {@link Command#getStartedAt() started} or
  *     {@link Command#getCompletedAt() completed}, and its result, either a {@link Command#getResult() return value}
  *     or an {@link Command#getException() exception}.  Also captures a stack of {@link ActionDomainEvent}s.
  * </p>
  *
  * <p>
- *     Note that when invoking an action, other actions may be invoked courtesy of the {@link WrapperFactory}.  These
- *     "sub-actions" do <i>not</i> modify the contents of the command object; in other words think of the command
+ *     Note that when invoking an action, other actions may be invoked courtesy
+ *     of the {@link WrapperFactory}.  These "sub-actions" do <i>not</i> modify
+ *     the contents of the current command object; in other words think of the command
  *     object as representing the outer-most originating action.
  * </p>
  *
  * <p>
- *     <b>NOTE</b>: in Isis v1.x, one of the responsibilities of {@link Command} was to generate unique sequence numbers for
- *     a given transactionId, where there were three possible sequences that might be generated.
- *     <ul>
- *         <li>
- *         <p>the sequence of changed domain objects being published by the
- *         {@link org.apache.isis.applib.services.publish.PublisherService#publish(Interaction.Execution)}
- *         </p>
- *         <p>
- *         In v2 ... TODO[2158] - what replaces this?
- *         </p>
- *         </li>
- *
- *         <li>
- *         <p>
- *         The sequence of wrapped action invocations (each being published)
- *         </p>
- *         <p>
- *         In v2 this is now done by {@link Interaction#next(String) Interaction} itself.
- *         </p>
- *         </li>
- *
- *         <li>
- *         <p>
- *         and finally, one or more background commands that might be scheduled via the <code>BackgroundService</code>.
- *         </p>
- *         <p>
- *         In v2 ... TODO[2158] - what replaces this?
- *         </p>
- *         </li>
- *     </ul>
- *
+ *     That said, if the sub-action is invoked asynchronously (using
+ *     {@link WrapperFactory#asyncWrap(Object, AsyncControl)} or
+ *     {@link WrapperFactory#asyncWrapMixin(Class, Object, AsyncControl)}), then
+ *     a separate {@link Command} object
+ *     is created, and the originating {@link Command} is set to be its
+ *     {@link Command#getParent() parent}.
  * </p>
- *
  */
 // tag::refguide[]
-public interface Command extends HasUniqueId {
+public class Command implements HasUniqueId, HasUsername, HasCommandDto {
 
     // end::refguide[]
     /**
-     * The user that created the command.
-     */
-    // tag::refguide[]
-    String getUser();                           // <.>
-    // end::refguide[]
-
-    /**
-     * The date/time at which this command was created.
-     */
-    // tag::refguide[]
-    Timestamp getTimestamp();                   // <.>
-    // end::refguide[]
-
-    /**
-     * {@link Bookmark} of the target object (entity or service) on which this action was performed.
+     * Unique identifier for the command.
      *
      * <p>
-     * Will only be populated if a {@link BookmarkService} has been configured.
+     *     Derived from {@link #getCommandDto()}'s {@link CommandDto#getTransactionId()}
      * </p>
      */
+    @Override
     // tag::refguide[]
-    Bookmark getTarget();                       // <.>
-    // end::refguide[]
-
-    /**
-     * Holds a string representation of the invoked action, or the edited property, equivalent to
-     * {@link Identifier#toClassAndNameIdentityString()}.
-     */
-    // tag::refguide[]
-    String getMemberIdentifier();               // <.>
-    // end::refguide[]
-
+    public UUID getUniqueId() {                 // <.>
+        // ...
+        // end::refguide[]
+        return commandDto != null
+                ? UUID.fromString(commandDto.getTransactionId())
+                : null;
+    }
     /**
-     * A human-friendly description of the class of the target object.
+     * The user that created the command.
+     *
+     * <p>
+     *     Derived from {@link #getCommandDto()}'s {@link CommandDto#getUser()}
+     * </p>
      */
+    @Override
     // tag::refguide[]
-    String getTargetClass();                    // <.>
-    // end::refguide[]
+    public String getUsername() {               // <.>
+        // ...
+        // end::refguide[]
+        return commandDto != null
+                ? commandDto.getUser()
+                : null;
+    }
 
     /**
-     * The human-friendly name of the action invoked/property edited on the target object.
+     * The date/time at which this command was created.
+     *
+     * <p>
+     *     Derived from {@link #getCommandDto()}'s {@link CommandDto#getTimestamp()}.
+     * </p>
      */
     // tag::refguide[]
-    String getTargetAction();                   // <.>
-    // end::refguide[]
+    public Timestamp getTimestamp() {           // <.>
+        // ...
+        // end::refguide[]
+        return commandDto != null
+                ? JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimestamp())
+                : null;
+    }
 
     /**
-     * A human-friendly description of the arguments with which the action was invoked.
+     * Serializable representation of the action invocation/property edit.
+     *
+     * <p>
+     *     When the framework sets this (through an internal API), it is
+     *     expected to have {@link CommandDto#getTransactionId()},
+     *     {@link CommandDto#getUser()}, {@link CommandDto#getTimestamp()},
+     *     {@link CommandDto#getTargets()} and {@link CommandDto#getMember()}
+     *     to be populated.  The {@link #getUniqueId()}, {@link #getUsername()},
+     *     {@link #getTimestamp()} and {@link #getTarget()} are all derived
+     *     from the provided {@link CommandDto}.
+     * </p>
      */
     // tag::refguide[]
-    String getArguments();                      // <.>
+    @Getter
+    private CommandDto commandDto;              // <.>
     // end::refguide[]
 
     /**
-     *
-     * A formal (XML or similar) specification of the action to invoke/being invoked.
+     * Derived from {@link #getCommandDto()}, is the {@link Bookmark} of
+     * the target object (entity or service) on which this action/edit was performed.
      */
     // tag::refguide[]
-    String getMemento();                        // <.>
+    public Bookmark getTarget() {               // <.>
+        return commandDto != null
+                ? Bookmark.from(commandDto.getTargets().getOid().get(0))
+                : null;
+    }
     // end::refguide[]
 
     /**
-     *
-     * The mechanism by which this command is to be executed, either synchronously &quot;in the
-     * {@link CommandExecuteIn#FOREGROUND foreground}&quot; or is to be executed asynchronously &quot;in the
-     * {@link CommandExecuteIn#BACKGROUND background}&quot; through the {@link BackgroundCommandService}.
+     * Derived from {@link #getCommandDto()}, holds a string
+     * representation of the invoked action, or the edited property.
      */
     // tag::refguide[]
-    CommandExecuteIn getExecuteIn();            // <.>
-    // end::refguide[]
-
-    // tag::refguide2[]
-    enum Executor {
-        // end::refguide2[]
-        /**
-         * Command being executed by the end-user.
-         */
-        // tag::refguide2[]
-        USER,
-        // end::refguide2[]
-        /**
-         * Command being executed by a background execution service.
-         */
-        // tag::refguide2[]
-        BACKGROUND,
-        // end::refguide2[]
-        /**
-         * Command being executed for some other reason, eg as result of redirect-after-post, or the homePage action.
-         */
-        // tag::refguide2[]
-        OTHER
+    public String getLogicalMemberIdentifier() {    // <.>
+        return commandDto != null
+                    ? commandDto.getMember().getLogicalMemberIdentifier()
+                    : null;
     }
-    // end::refguide2[]
+    // end::refguide[]
 
     /**
-     * The (current) executor of this command.
+     * For async commands created through the {@link WrapperFactory},
+     * captures the parent command.
      *
      * <p>
-     * Note that (even for implementations of {@link BackgroundCommandService} that persist {@link Command}s), this
-     * property is never (likely to be) persisted, because it is always updated to indicate how the command is
-     * currently being executed.
+     *     Will return <code>null</code> if there is no parent.
+     * </p>
      *
-     * <p>
-     * If the {@link #getExecutor() executor} matches the required {@link #getExecuteIn() execution policy}, then the
-     * command actually is executed.  The combinations are:
-     * <ul>
-     * <li>executor = USER, executeIn = FOREGROUND, then execute</li>
-     * <li>executor = USER, executeIn = BACKGROUND, then persist and return persisted command as a placeholder for the result</li>
-     * <li>executor = BACKGROUND, executeIn = FOREGROUND, then ignore</li>
-     * <li>executor = BACKGROUND, executeIn = BACKGROUND, then execute, update the command with result</li>
-     * </ul>
+     * @see WrapperFactory#asyncWrap(Object, AsyncControl)
+     * @see WrapperFactory#asyncWrapMixin(Class, Object, AsyncControl)
      *
      */
     // tag::refguide[]
-    Executor getExecutor();                     // <.>
+    @Getter
+    private Command parent;                     // <.>
     // end::refguide[]
 
     /**
-     * For an command that has actually been executed, holds the date/time at which the {@link Interaction} that
-     * executed the command started.
+     * For an command that has actually been executed, holds the date/time at
+     * which the {@link Interaction} that executed the command started.
      *
-     * <p>
-     *     Previously this field was deprecated (on the basis that the startedAt is also held in
-     *     {@link Interaction.Execution#getStartedAt()}). However, this property is now used in master/slave
-     *     replay scenarios which may query a persisted Command.
-     * </p>
-     *
-     * See also {@link Interaction#getCurrentExecution()} and
-     * {@link Interaction.Execution#getStartedAt()}.
+     * @see Interaction#getCurrentExecution()
+     * @see Interaction.Execution#getStartedAt()
      */
     // tag::refguide[]
-    Timestamp getStartedAt();                   // <.>
+    @Getter
+    private Timestamp startedAt;                // <.>
     // end::refguide[]
 
     /**
@@ -246,224 +204,144 @@ public interface Command extends HasUniqueId {
      * {@link Interaction.Execution#getCompletedAt()}.
      */
     // tag::refguide[]
-    Timestamp getCompletedAt();                 // <.>
+    @Getter
+    private Timestamp completedAt;              // <.>
     // end::refguide[]
 
     /**
-     * For actions created through the {@link BackgroundService} and {@link BackgroundCommandService},
-     * captures the parent action.
-     */
-    // tag::refguide[]
-    Command getParent();                        // <.>
-    // end::refguide[]
-
-    /**
-     * For an command that has actually been executed, holds the exception stack
-     * trace if the action invocation/property modification threw an exception.
+     * For a command that has actually been executed, holds a {@link Bookmark}
+     * to the object returned by the corresponding action/property modification.
      *
      * <p>
-     *     Previously this field was deprecated (on the basis that the exception is also held in
-     *     {@link Interaction.Execution#getThrew()}). However, this property is now used in master/slave
-     *     replay scenarios which may query a persisted Command.
+     *     This property is used in replay scenarios to verify the outcome of
+     *     the replayed command, eg for regression testing.
      * </p>
      *
-     * See also {@link Interaction#getCurrentExecution()} and  {@link org.apache.isis.applib.services.iactn.Interaction.Execution#getThrew()}.
+     * See also  {@link Interaction#getCurrentExecution()} and
+     * {@link org.apache.isis.applib.services.iactn.Interaction.Execution#getReturned()}.
      */
     // tag::refguide[]
-    String getException();                      // <.>
+    @Getter
+    private Bookmark result;                    // <.>
     // end::refguide[]
 
     /**
-     * For an command that has actually been executed, holds a {@link Bookmark} to the object returned by the corresponding action/property modification.
+     * For a command that has actually been executed, holds the exception stack
+     * trace if the action invocation/property modification threw an exception.
      *
      * <p>
-     *     Previously this field was deprecated (on the basis that the returned value is also held in
-     *     {@link Interaction.Execution#getReturned()}). However, this property is now used in master/slave
-     *     replay scenarios which may query a persisted Command.
+     *     This property is used in replay scenarios to verify the outcome of
+     *     the replayed command, eg for regression testing.
      * </p>
      *
-     * See also  {@link Interaction#getCurrentExecution()} and  {@link org.apache.isis.applib.services.iactn.Interaction.Execution#getReturned()}.
+     * See also {@link Interaction#getCurrentExecution()} and
+     * {@link org.apache.isis.applib.services.iactn.Interaction.Execution#getThrew()}.
      */
     // tag::refguide[]
-    Bookmark getResult();                       // <.>
+    @Getter
+    private Throwable exception;                    // <.>
     // end::refguide[]
 
     /**
-     * Whether this command should ultimately be persisted (if the configured {@link BackgroundCommandService} supports
-     * it) or not.
+     * Whether this command resulted in a change of state to the system.
      *
      * <p>
-     * If the action to be executed has been annotated with the {@link Action#command()} attribute
-     * then (unless its {@link Action#commandPersistence()} persistence} attribute has been set to a different value
-     * than its default of {@link org.apache.isis.applib.annotation.CommandPersistence#PERSISTED persisted}), the
-     * {@link Command} object will be persisted.
-     *
-     * <p>
-     * However, it is possible to prevent the {@link Command} object from ever being persisted by setting the
-     * {@link org.apache.isis.applib.annotation.Action#commandPersistence() persistence} attribute to
-     * {@link org.apache.isis.applib.annotation.CommandPersistence#NOT_PERSISTED}, or it can be set to
-     * {@link org.apache.isis.applib.annotation.CommandPersistence#IF_HINTED}, meaning it is dependent
-     * on whether {@link #setPersistHint(boolean) a hint has been set} by some other means.
+     *     This can be used as a hint to decide whether to persist the command
+     *     to a datastore, for example for auditing (though
+     *     {@link org.apache.isis.applib.services.publish.PublisherService} is
+     *     an alternative for that use case) or so that it can be retrieved
+     *     and replayed on another system, eg for regression testing.
+     * </p>
      *
-     * <p>
-     * For example, a {@link BackgroundCommandService} implementation that creates persisted background commands ought
-     * associate them (via its {@link Command#getParent() parent}) to an original persisted
-     * {@link Command}.  The hinting mechanism allows the service to suggest that the parent command be persisted so
-     * that the app can then provide a mechanism to find all child background commands for that original parent command.
      */
     // tag::refguide[]
-    CommandPersistence getPersistence();        // <.>
+    @Getter
+    private boolean systemStateChanged;                // <.>
     // end::refguide[]
 
-    /**
-     * Whether that this {@link Command} should be persisted, if possible.
-     */
-    // tag::refguide[]
-    boolean isPersistHint();                    // <.>
-    // end::refguide[]
 
-    /**
-     * <b>NOT API</b>: intended to be called only by the framework.
-     */
-    public static interface Internal {
+    private final Updater UPDATER = new Updater();
 
+    public class Updater implements CommandOutcomeHandler {
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          *
          * <p>
-         * Implementation notes: set when the Isis PersistenceSession is opened.
-         */
-        void setUser(String user);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the Isis PersistenceSession is opened.
-         */
-        void setTimestamp(Timestamp timestamp);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the action is invoked (in the ActionInvocationFacet).
-         */
-        void setTarget(Bookmark target);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the action is invoked (in <tt>ActionInvocationFacet</tt>) or in
-         * property edited (in <tt>PropertySetterFacet</tt>).
-         */
-        void setMemberIdentifier(String memberIdentifier);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>) or property edited
-         * (in the <tt>PropertySetterOrClearFacet</tt>).
+         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>).
+         * @param commandDto
          */
-        void setTargetAction(String targetAction);
-
-
+        public void setCommandDto(final CommandDto commandDto, final int targetIdx) {
+            Command.this.commandDto = commandDto;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          *
          * <p>
-         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>).
+         *     Only populated for async commands created through the
+         *     {@link WrapperFactory}.
+         * </p>
          */
-        void setArguments(final String arguments);
-
+        public void setParent(Command parent) {
+            Command.this.parent = parent;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setExecutor(final Executor executor);
-
+        @Override
+        public Timestamp getStartedAt() {
+            return Command.this.getStartedAt();
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setResult(Bookmark resultBookmark);
-
+        @Override
+        public void setStartedAt(Timestamp startedAt) {
+            Command.this.startedAt = startedAt;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setException(String stackTrace);
-
+        @Override
+        public void setCompletedAt(final Timestamp completed) {
+            Command.this.completedAt = completed;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setParent(final Command parent);
+        @Override
+        public void setResult(final Bookmark result) {
+            Command.this.result = result;
+        }
 
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         *     Previously this field was deprecated (on the basis that the completedAt is also held in
-         *     {@link Interaction.Execution#getCompletedAt()}). However, this property is now used in master/slave
-         *     replay scenarios which may query a persisted Command.
-         * </p>
-         *
-         * See also {@link Interaction#getCurrentExecution()} and
-         * {@link Interaction.Execution#setCompletedAt(Timestamp)}.
          */
-        void setCompletedAt(Timestamp completedAt);
-
+        @Override
+        public void setException(final Throwable exception) {
+            Command.this.exception = exception;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          *
-         *
          * <p>
-         *     Previously this field was deprecated (on the basis that the completedAt is also held in
-         *     {@link Interaction.Execution#getCompletedAt()}). However, this property is now used in master/slave
-         *     replay scenarios which may query a persisted Command.
+         * Hint that this {@link Command} has resulted in a change of state to the system.
+         * Implementations can use this to persist the command, for example.
          * </p>
-         *
-         * See also {@link Interaction#getCurrentExecution()} and
-         * {@link #setStartedAt(org.apache.isis.applib.services.clock.ClockService, org.apache.isis.applib.services.metrics.MetricsService)}.
          */
-        void setStartedAt(Timestamp startedAt);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>).
-         */
-        void setMemento(final String memento);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * <p>
-         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</t>).
-         */
-        void setTargetClass(String targetClass);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         */
-        void setPersistence(final CommandPersistence persistence);
-
-        /**
-         * Hint that this {@link Command} should be persisted, if possible.
-         *
-         * <p>
-         * <b>NOT API</b>: intended to be called only by the framework.
-         *
-         * @see #getPersistence()
-         */
-        void setPersistHint(boolean persistHint);
-    }
+        public void setSystemStateChanged(boolean systemStateChanged) {
+            Command.this.systemStateChanged = systemStateChanged;
+        }
+    };
 
     /**
      * <b>NOT API</b>: intended to be called only by the framework.
      */
-    Internal internal();
+    public Updater updater() {
+        return UPDATER;
+    }
+
 
 // tag::refguide[]
+
 }
 // end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDefault.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDefault.java
deleted file mode 100644
index 069acd4..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDefault.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.command;
-
-import java.sql.Timestamp;
-import java.util.UUID;
-
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.util.ObjectContracts;
-import org.apache.isis.applib.util.ToString;
-
-import lombok.Getter;
-
-public class CommandDefault implements Command {
-
-    public CommandDefault() {
-        this.executor = Executor.OTHER;
-        this.uniqueId = UUID.randomUUID();
-    }
-
-    @Getter
-    private String memberIdentifier;
-
-    @Getter
-    private String targetClass;
-
-    @Getter
-    private String targetAction;
-
-    @Getter
-    private String arguments;
-
-    @Getter
-    private String memento;
-
-    @Getter
-    private Bookmark target;
-
-    @Getter
-    private Timestamp timestamp;
-
-    @Getter
-    private Timestamp startedAt;
-
-    @Getter
-    private Timestamp completedAt;
-
-    @Getter
-    private String user;
-
-    @Getter
-    private Executor executor;
-
-    @Getter
-    private CommandExecuteIn executeIn;
-
-    @Getter
-    private Command parent;
-
-    @Getter
-    private Bookmark result;
-
-    @Getter
-    private String exception;
-
-    @Getter
-    private UUID uniqueId;
-
-    @Getter
-    private CommandPersistence persistence;
-
-    @Getter
-    private boolean persistHint;
-
-    // -- toString
-
-    private static final ToString<CommandDefault> toString = ObjectContracts
-            .toString("startedAt", CommandDefault::getStartedAt)
-            .thenToString("user", CommandDefault::getUser)
-            .thenToString("memberIdentifier", CommandDefault::getMemberIdentifier)
-            .thenToString("target", CommandDefault::getTarget)
-            .thenToString("transactionId", CommandDefault::getUniqueId);
-
-    @Override
-    public String toString() {
-        return toString.toString(this);
-    }
-
-
-    // -- FRAMEWORK INTERNAL
-
-    private final Command.Internal INTERNAL = new Command.Internal() {
-        @Override
-        public void setMemberIdentifier(String actionIdentifier) {
-            CommandDefault.this.memberIdentifier = actionIdentifier;
-        }
-        @Override
-        public void setTargetClass(String targetClass) {
-            CommandDefault.this.targetClass = targetClass;
-        }
-        @Override
-        public void setTargetAction(String targetAction) {
-            CommandDefault.this.targetAction = targetAction;
-        }
-        @Override
-        public void setArguments(String arguments) {
-            CommandDefault.this.arguments = arguments;
-        }
-        @Override
-        public void setMemento(String memento) {
-            CommandDefault.this.memento = memento;
-        }
-        @Override
-        public void setTarget(Bookmark target) {
-            CommandDefault.this.target = target;
-        }
-        @Override
-        public void setTimestamp(Timestamp timestamp) {
-            CommandDefault.this.timestamp = timestamp;
-        }
-        @Override
-        public void setStartedAt(Timestamp startedAt) {
-            CommandDefault.this.startedAt = startedAt;
-        }
-        @Override
-        public void setCompletedAt(final Timestamp completed) {
-            CommandDefault.this.completedAt = completed;
-        }
-        @Override
-        public void setUser(String user) {
-            CommandDefault.this.user = user;
-        }
-        @Override
-        public void setParent(Command parent) {
-            CommandDefault.this.parent = parent;
-        }
-        @Override
-        public void setResult(final Bookmark result) {
-            CommandDefault.this.result = result;
-        }
-        @Override
-        public void setException(final String exceptionStackTrace) {
-            CommandDefault.this.exception = exceptionStackTrace;
-        }
-        @Override
-        public void setPersistence(CommandPersistence persistence) {
-            CommandDefault.this.persistence = persistence;
-        }
-        @Override
-        public void setPersistHint(boolean persistHint) {
-            CommandDefault.this.persistHint = persistHint;
-        }
-        @Override
-        public void setExecutor(Executor executor) {
-            CommandDefault.this.executor = executor;
-        }
-    };
-
-    @Override
-    public Command.Internal internal() {
-        return INTERNAL;
-    }
-
-}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandExecutorService.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandExecutorService.java
index 56d029b..4fce410 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandExecutorService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandExecutorService.java
@@ -48,18 +48,27 @@ public interface CommandExecutorService {
      * Executes the specified command.
      *
      * @param sudoPolicy
-     * @param commandWithDto
+     * @param command
      * @return - any exception raised by the command.
      */
     // tag::refguide[]
-    void executeCommand(
-            SudoPolicy sudoPolicy,          // <.>
-            CommandWithDto commandWithDto   // <.>
+    Bookmark executeCommand(
+            SudoPolicy sudoPolicy,                  // <.>
+            Command command                         // <.>
     );
 
     Bookmark executeCommand(
-            CommandDto commandDto           // <.>
+            SudoPolicy sudoPolicy,                  // <.>
+            CommandDto commandDto,                  // <.>
+            CommandOutcomeHandler outcomeHandler);  // <.>
+
+    Bookmark executeCommand(
+            Command command                         // <.>
     );
 
+    Bookmark executeCommand(
+            CommandDto commandDto,                  // <.>
+            CommandOutcomeHandler outcomeHandler);  // <.>
+
 }
 // end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandOutcomeHandler.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandOutcomeHandler.java
new file mode 100644
index 0000000..ccd9530
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandOutcomeHandler.java
@@ -0,0 +1,24 @@
+package org.apache.isis.applib.services.command;
+
+import java.sql.Timestamp;
+
+import org.apache.isis.applib.services.bookmark.Bookmark;
+
+public interface CommandOutcomeHandler {
+
+    CommandOutcomeHandler NULL = new CommandOutcomeHandler() {
+        @Override public Timestamp getStartedAt() { return null; }
+        @Override public void setStartedAt(Timestamp startedAt) { }
+        @Override public void setCompletedAt(Timestamp completedAt) { }
+        @Override public void setResult(Bookmark resultBookmark) { }
+        @Override public void setException(Throwable throwable) { }
+    };
+
+    Timestamp getStartedAt();
+    void setStartedAt(Timestamp startedAt);
+
+    void setCompletedAt(Timestamp completedAt);
+
+    void setResult(Bookmark resultBookmark);
+    void setException(Throwable throwable);
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandService.java
similarity index 53%
rename from api/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java
rename to api/applib/src/main/java/org/apache/isis/applib/services/command/CommandService.java
index 4d07f9d..eae10f0 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandContext.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandService.java
@@ -18,7 +18,8 @@
  */
 package org.apache.isis.applib.services.command;
 
-import java.util.Optional;
+import java.sql.Timestamp;
+import java.util.List;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -30,51 +31,52 @@ import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.IsisInteractionScope;
 import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.services.command.Command.Executor;
-import org.apache.isis.applib.services.inject.ServiceInjector;
+import org.apache.isis.applib.services.clock.ClockService;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.command.spi.CommandServiceListener;
+import org.apache.isis.applib.services.user.UserService;
 
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
-/**
- * This service (API and implementation) provides access to context information about any {@link Command}.
- *
- * This implementation has no UI and there is only one implementation (this class) in applib, so it is annotated with
- * {@link org.apache.isis.applib.annotation.DomainService}.  This means that it is automatically registered and
- * available for use; no further configuration is required.
- */
-// tag::refguide[]
 @Service
-@Named("isisApplib.CommandContext")
+@Named("isisApplib.CommandService")
 @Order(OrderPrecedence.MIDPOINT)
 @Primary
 @Qualifier("Default")
-@IsisInteractionScope
-@RequiredArgsConstructor(onConstructor_ = {@Inject})
-//@Log4j2
-public class CommandContext {
-
-    private final ServiceInjector serviceInjector;
-    
-    @Getter
-    private Command command;
+@Log4j2
+// tag::refguide[]
+public class CommandService {
 
     // end::refguide[]
     /**
-     * <b>NOT API</b>: intended to be called only by the framework.
+     * &quot;Complete&quot; the command, providing an opportunity ot persist
+     * a memento of the command if the
+     * {@link Command#isSystemStateChanged() system state has changed}.
+     *
+     * <p>
+     *     The framework will automatically have set the {@link Command#getCompletedAt()} property.
+     * </p>
      */
-    public void setCommand(final Command command) {
-        this.command = command;
-        if(command!=null) {
-            serviceInjector.injectServicesInto(command);
+    // tag::refguide[]
+    public void complete(final Command command) {   // <.>
+        // ...
+    // end::refguide[]
+
+        if(command.getLogicalMemberIdentifier() == null) {
+            // eg if seed fixtures
+            return;
         }
-    }
 
-    public Optional<Executor> getCurrentExecutor() {
-        return Optional.ofNullable(getCommand())
-                .map(Command::getExecutor);
-    }
+        log.debug("complete: {}, systemStateChanged {}",
+                command.getLogicalMemberIdentifier(),
+                command.isSystemStateChanged());
 
     // tag::refguide[]
+        commandServiceListeners.forEach(x -> x.onComplete(command));
+    }
+
+    @Inject List<CommandServiceListener> commandServiceListeners;
+
 }
 // end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandWithDto.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandWithDto.java
deleted file mode 100644
index cd421ea..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandWithDto.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.command;
-
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-
-// tag::refguide[]
-public interface CommandWithDto extends Command {
-
-    String USERDATA_KEY_TARGET_CLASS = "targetClass";
-    String USERDATA_KEY_TARGET_ACTION = "targetAction";
-    String USERDATA_KEY_ARGUMENTS = "arguments";
-    String USERDATA_KEY_RETURN_VALUE = "returnValue";
-    String USERDATA_KEY_EXCEPTION = "exception";
-
-    CommandDto asDto();
-}
-// end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/package-info.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/package-info.java
index 99c057a..e044456 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/package-info.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/command/package-info.java
@@ -17,11 +17,4 @@
  *  under the License.
  */
 
-/**
- * The {@link org.apache.isis.applib.services.command.CommandContext} service is a request-scoped service that reifies
- * the invocation of an action on a domain object into an object itself. This reified information is encapsulated
- * within the {@link org.apache.isis.applib.services.command.Command} object.
- *
- *
- */
 package org.apache.isis.applib.services.command;
\ No newline at end of file
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/spi/CommandService.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/spi/CommandService.java
deleted file mode 100644
index eb3abce..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/spi/CommandService.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.command.spi;
-
-import org.apache.isis.applib.services.command.Command;
-
-/**
- * Default factory service for {@link Command}s.
- */
-// tag::refguide[]
-public interface CommandService {
-
-    // end::refguide[]
-    /**
-     * Simply instantiates the appropriate instance of the {@link Command}.
-     *
-     * <p>
-     * Its members will be populated automatically by the framework (the {@link Command}'s
-     * {@link Command#getTimestamp()}, {@link Command#getUser()} and {@link Command#getUniqueId()}).
-     * </p>
-     */
-    // tag::refguide[]
-    Command create();                               // <.>
-    // end::refguide[]
-
-    /**
-     * Hint for this implementation to eagerly persist the {@link Command}s if possible; influences the behaviour
-     * of actions annotated to execute in the {@link org.apache.isis.applib.annotation.CommandExecuteIn#BACKGROUND}.
-     */
-    // tag::refguide[]
-    boolean persistIfPossible(Command command);     // <.>
-    // end::refguide[]
-
-    /**
-     * &quot;Complete&quot; the command, typically meaning to indicate that the command is completed, and to
-     * persist it if its {@link Command#getPersistence()} and {@link Command#isPersistHint() persistence hint}
-     * indicate that it should be.
-     *
-     * <p>
-     * However, not every implementation necessarily {@link #persistIfPossible(Command) supports persistence}.
-     *
-     * <p>
-     *     The framework will automatically have set the {@link Command#getCompletedAt()} property.
-     * </p>
-     */
-    // tag::refguide[]
-    void complete(final Command command);           // <.>
-}
-// end::refguide[]
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandServiceDefault.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/spi/CommandServiceListener.java
similarity index 56%
rename from core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandServiceDefault.java
rename to api/applib/src/main/java/org/apache/isis/applib/services/command/spi/CommandServiceListener.java
index d9f3c00..ba6b727 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandServiceDefault.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/command/spi/CommandServiceListener.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.core.runtimeservices.command;
+package org.apache.isis.applib.services.command.spi;
 
 import javax.inject.Named;
 
@@ -27,29 +27,41 @@ import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandDefault;
-import org.apache.isis.applib.services.command.spi.CommandService;
-
-@Service
-@Named("isisRuntimeServices.CommandServiceDefault")
-@Order(OrderPrecedence.MIDPOINT)
-@Primary
-@Qualifier("Default")
-public class CommandServiceDefault implements CommandService {
-
-    @Override
-    public Command create() {
-        return new CommandDefault();
-    }
 
-    @Override
-    public void complete(final Command command) {
-        // nothing to do
-    }
+import lombok.extern.log4j.Log4j2;
 
-    @Override
-    public boolean persistIfPossible(final Command command) {
-        return false;
-    }
+/**
+ * SPI
+ */
+// tag::refguide[]
+public interface CommandServiceListener {
 
+    /**
+     * Notifies that the command has completed.
+     *
+     * <p>
+     *     This is an opportunity for implementations to process the command,
+     *     for example to persist a representation of it.
+     * </p>
+     */
+    // tag::refguide[]
+    void onComplete(final Command command);           // <.>
+
+    /**
+     * At least one implementation is required to satisfy injection point into
+     * {@link org.apache.isis.applib.services.command.CommandService}.
+     */
+    @Service
+    @Named("isisApplib.CommandServiceListenerNull")
+    @Order(OrderPrecedence.LATE)
+    @Qualifier("Null")
+    @Log4j2
+    public static class Null implements CommandServiceListener {
+
+        @Override
+        public void onComplete(Command command) {
+
+        }
+    }
 }
+// end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/HasCommandDto.java b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/HasCommandDto.java
new file mode 100644
index 0000000..6c9d631
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/HasCommandDto.java
@@ -0,0 +1,12 @@
+package org.apache.isis.applib.services.commanddto;
+
+import org.apache.isis.schema.cmd.v2.CommandDto;
+
+/**
+ * Objects implementing this interface will be processed automatically by
+ * {@link org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto}.
+ */
+public interface HasCommandDto {
+
+    CommandDto getCommandDto();
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/conmap/ContentMappingServiceForCommandDto.java b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/conmap/ContentMappingServiceForCommandDto.java
new file mode 100644
index 0000000..6e1edfb
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/conmap/ContentMappingServiceForCommandDto.java
@@ -0,0 +1,106 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.applib.services.commanddto.conmap;
+
+import java.sql.Timestamp;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.ws.rs.core.MediaType;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.commanddto.HasCommandDto;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
+import org.apache.isis.applib.services.conmap.ContentMappingService;
+import org.apache.isis.applib.services.commanddto.processor.spi.CommandDtoProcessorService;
+import org.apache.isis.applib.services.metamodel.MetaModelService;
+import org.apache.isis.applib.util.schema.CommandDtoUtils;
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
+import org.apache.isis.schema.cmd.v2.CommandDto;
+import org.apache.isis.schema.common.v2.PeriodDto;
+
+import lombok.val;
+
+@Service
+@Named("isisApplib.ContentMappingServiceForCommandDto")
+@Order(OrderPrecedence.EARLY)
+@Primary
+@Qualifier("CommandDto")
+public class ContentMappingServiceForCommandDto implements ContentMappingService {
+
+    @Override
+    public Object map(Object object, final List<MediaType> acceptableMediaTypes) {
+        final boolean supported = Util.isSupported(CommandDto.class, acceptableMediaTypes);
+        if(!supported) {
+            return null;
+        }
+
+        return asProcessedDto(object);
+    }
+
+    CommandDto asProcessedDto(final Object object) {
+        val commandDto = asCommandDto(object);
+        return asProcessedDto(object, commandDto);
+    }
+
+    private CommandDto asCommandDto(Object object) {
+        if(object instanceof CommandDto) {
+            return (CommandDto) object;
+        }
+        if(object instanceof HasCommandDto) {
+            return ((HasCommandDto) object).getCommandDto();
+        }
+        return null;
+    }
+
+    private CommandDto asProcessedDto(final Object domainObject, CommandDto commandDto) {
+
+        // global processors
+        for (final CommandDtoProcessorService commandDtoProcessorService : commandDtoProcessorServices) {
+            commandDto = commandDtoProcessorService.process(domainObject, commandDto);
+            if(commandDto == null) {
+                // any processor could return null, effectively breaking the chain.
+                return null;
+            }
+        }
+
+        // specific processor for this specific member (action or property)
+        val logicalMemberId = commandDto.getMember().getLogicalMemberIdentifier();
+        final CommandDtoProcessor commandDtoProcessor =
+                metaModelService.commandDtoProcessorFor(logicalMemberId);
+        if (commandDtoProcessor == null) {
+            return commandDto;
+        }
+        return commandDtoProcessor.process(commandDto);
+    }
+
+
+    @Inject MetaModelService metaModelService;
+    @Inject List<CommandDtoProcessorService> commandDtoProcessorServices;
+
+}
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandsDto.java b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/conmap/ContentMappingServiceForCommandsDto.java
similarity index 98%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandsDto.java
rename to api/applib/src/main/java/org/apache/isis/applib/services/commanddto/conmap/ContentMappingServiceForCommandsDto.java
index ade5070..4d491b2 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/ContentMappingServiceForCommandsDto.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/conmap/ContentMappingServiceForCommandsDto.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.conmap.command;
+package org.apache.isis.applib.services.commanddto.conmap;
 
 import java.util.List;
 
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/background/package-info.java b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/conmap/UserDataKeys.java
similarity index 65%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/background/package-info.java
rename to api/applib/src/main/java/org/apache/isis/applib/services/commanddto/conmap/UserDataKeys.java
index f63812d..4fc4e59 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/background/package-info.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/conmap/UserDataKeys.java
@@ -16,12 +16,19 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
+package org.apache.isis.applib.services.commanddto.conmap;
+
+import org.apache.isis.schema.cmd.v2.CommandDto;
+
+import lombok.experimental.UtilityClass;
 
 /**
- * The {@link org.apache.isis.applib.services.background.BackgroundService2} domain service, and also the companion
- * {@link org.apache.isis.applib.services.background.BackgroundCommandService2} SPI service, enable commands to be
- * persisted such that they may be invoked in the background
- *
- *
+ * Keys used in {@link CommandDto#getUserData()} to marshall the command's results
  */
-package org.apache.isis.applib.services.background;
\ No newline at end of file
+@UtilityClass
+public class UserDataKeys {
+
+    public static String RESULT = UserDataKeys.class.getName() + "#" + "RESULT";
+    public static String EXCEPTION = UserDataKeys.class.getName() + "#" + "EXCEPTION";
+
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessor.java b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/CommandDtoProcessor.java
similarity index 57%
rename from api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessor.java
rename to api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/CommandDtoProcessor.java
index 3c7336e..a0706ad 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessor.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/CommandDtoProcessor.java
@@ -16,34 +16,42 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.command;
+package org.apache.isis.applib.services.commanddto.processor;
 
-import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
+/**
+ * Refine (or possibly ignore) a command when replicating from primary
+ * to secondary.
+ */
 // tag::refguide[]
 public interface CommandDtoProcessor {
 
     // end::refguide[]
     /**
-     * Returning <tt>null</tt> means that the command's DTO is effectively excluded from any list.
-     * If replicating from master to slave, this allows commands that can't be replicated to be ignored.
-     * @param command
-     * @param commandDto
+     * The implementation can if necessary refine or alter the
+     * {@link CommandDto} to be replicated from primary to secondary.
+     *
+     * <p>
+     *     That said, the most common use case is to return <code>null</code>,
+     *     which results in the command effectively being ignore.
+     * </p>
+     *
+     * @param commandDto - to be processed
+     * @return <tt>null</tt> means that the command's DTO is effectively
+     *         excluded.
      */
     // tag::refguide[]
-    CommandDto process(final Command command, CommandDto commandDto);   // <.>
+    CommandDto process(CommandDto commandDto);   // <.>
 
     // end::refguide[]
     /**
-     * Convenience implementation to simply indicate that no DTO should be returned for a command,
-     * effectively ignoring it for replay purposes.
+     * Convenience implementation to simply indicate that no DTO should be
+     * returned for a command, effectively ignoring it for replication purposes.
      */
-    public static class Null implements CommandDtoProcessor {
+    class Null implements CommandDtoProcessor {
         @Override
-        public CommandDto process(
-                final Command command,
-                final CommandDto commandDto) {
+        public CommandDto process(final CommandDto commandDto) {
             return null;
         }
     }
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForActionAbstract.java b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/CommandDtoProcessorForActionAbstract.java
similarity index 90%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForActionAbstract.java
rename to api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/CommandDtoProcessorForActionAbstract.java
index 531c945..b887323 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/command/CommandDtoProcessorForActionAbstract.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/CommandDtoProcessorForActionAbstract.java
@@ -16,8 +16,9 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.command;
+package org.apache.isis.applib.services.commanddto.processor;
 
+import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.schema.cmd.v2.ActionDto;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.ParamDto;
@@ -27,9 +28,6 @@ import org.apache.isis.schema.cmd.v2.ParamsDto;
  * Convenience adapter for command processors for action invocations.
  */
 public abstract class CommandDtoProcessorForActionAbstract implements CommandDtoProcessor {
-    protected CommandDto asDto(final CommandWithDto commandWithDto) {
-        return commandWithDto.asDto();
-    }
     protected ActionDto getActionDto(final CommandDto commandDto) {
         return (ActionDto) commandDto.getMember();
     }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessorForPropertyAbstract.java b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/CommandDtoProcessorForPropertyAbstract.java
similarity index 88%
rename from api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessorForPropertyAbstract.java
rename to api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/CommandDtoProcessorForPropertyAbstract.java
index 6bc4d69..e833809 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessorForPropertyAbstract.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/CommandDtoProcessorForPropertyAbstract.java
@@ -16,8 +16,9 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.command;
+package org.apache.isis.applib.services.commanddto.processor;
 
+import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.PropertyDto;
 
@@ -26,9 +27,6 @@ import org.apache.isis.schema.cmd.v2.PropertyDto;
  */
 public abstract class CommandDtoProcessorForPropertyAbstract
 implements CommandDtoProcessor {
-    protected CommandDto asDto(final CommandWithDto commandWithDto) {
-        return commandWithDto.asDto();
-    }
     protected PropertyDto getPropertyDto(final CommandDto commandDto) {
         return (PropertyDto) commandDto.getMember();
     }
diff --git a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/spi/CommandDtoProcessorService.java b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/spi/CommandDtoProcessorService.java
similarity index 57%
rename from api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/spi/CommandDtoProcessorService.java
rename to api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/spi/CommandDtoProcessorService.java
index 48439df..c645d04 100644
--- a/api/applib/src/main/adoc/modules/applib-svc/examples/services/conmap/command/spi/CommandDtoProcessorService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/spi/CommandDtoProcessorService.java
@@ -16,12 +16,17 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.conmap.command.spi;
+package org.apache.isis.applib.services.commanddto.processor.spi;
 
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandDtoProcessor;
-import org.apache.isis.applib.services.conmap.command.ContentMappingServiceForCommandDto;
+import javax.annotation.Nullable;
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
+import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceForCommandDto;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
 /**
@@ -32,7 +37,16 @@ import org.apache.isis.schema.cmd.v2.CommandDto;
 // tag::refguide[]
 public interface CommandDtoProcessorService {
 
-    CommandDto process(final Command command, CommandDto commandDto);
+    /**
+     * @param domainObject - is the target that acts as the source of the
+     *                       {@link CommandDto}.
+     * @param commandDto - is either <code>null</code>, or is passed from a
+     *                     previous implementation for further refinement.
+     * @return
+     */
+    CommandDto process(final Object domainObject, @Nullable final CommandDto commandDto);
+
 
 }
 // end::refguide[]
+
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/spi/CommandDtoProcessorServiceIdentity.java b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/spi/CommandDtoProcessorServiceIdentity.java
new file mode 100644
index 0000000..aee41b0
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/commanddto/processor/spi/CommandDtoProcessorServiceIdentity.java
@@ -0,0 +1,23 @@
+package org.apache.isis.applib.services.commanddto.processor.spi;
+
+import javax.inject.Named;
+
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+
+import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.schema.cmd.v2.CommandDto;
+
+/**
+ * At least one implementation is required.
+ */
+@Service
+@Named("isisApplib.CommandDtoProcessorServiceIdentity")
+@Order(OrderPrecedence.LAST)
+public class CommandDtoProcessorServiceIdentity implements CommandDtoProcessorService {
+
+    @Override
+    public CommandDto process(final Object domainObject, final CommandDto commandDto) {
+        return commandDto;
+    }
+}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/conmap/command/ContentMappingServiceForCommandDto.java b/api/applib/src/main/java/org/apache/isis/applib/services/conmap/command/ContentMappingServiceForCommandDto.java
deleted file mode 100644
index 480cb7b..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/services/conmap/command/ContentMappingServiceForCommandDto.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.conmap.command;
-
-import java.sql.Timestamp;
-import java.util.List;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.ws.rs.core.MediaType;
-
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.context.annotation.Primary;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-
-import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandDtoProcessor;
-import org.apache.isis.applib.services.command.CommandWithDto;
-import org.apache.isis.applib.services.conmap.ContentMappingService;
-import org.apache.isis.applib.services.conmap.command.spi.CommandDtoProcessorService;
-import org.apache.isis.applib.services.metamodel.MetaModelService;
-import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-import org.apache.isis.schema.common.v2.PeriodDto;
-
-@Service
-@Named("isisApplib.ContentMappingServiceForCommandDto")
-@Order(OrderPrecedence.EARLY)
-@Primary
-@Qualifier("CommandDto")
-public class ContentMappingServiceForCommandDto implements ContentMappingService {
-
-    @Override
-    public Object map(Object object, final List<MediaType> acceptableMediaTypes) {
-        final boolean supported = Util.isSupported(CommandDto.class, acceptableMediaTypes);
-        if(!supported) {
-            return null;
-        }
-
-        return asProcessedDto(object);
-    }
-
-    /**
-     * Not part of the {@link ContentMappingService} API.
-     */
-    public CommandDto map(final CommandWithDto commandWithDto) {
-        return asProcessedDto(commandWithDto);
-    }
-
-    CommandDto asProcessedDto(final Object object) {
-        if (!(object instanceof CommandWithDto)) {
-            return null;
-        }
-        final CommandWithDto commandWithDto = (CommandWithDto) object;
-        return asProcessedDto(commandWithDto);
-    }
-
-    private CommandDto asProcessedDto(final CommandWithDto commandWithDto) {
-        if(commandWithDto == null) {
-            return null;
-        }
-        CommandDto commandDto = commandWithDto.asDto();
-
-        // global processors
-        for (final CommandDtoProcessorService commandDtoProcessorService : commandDtoProcessorServices) {
-            commandDto = commandDtoProcessorService.process(commandWithDto, commandDto);
-            if(commandDto == null) {
-                // any processor could return null, effectively breaking the chain.
-                return null;
-            }
-        }
-
-        // specific processors for this specific member (action or property)
-        final CommandDtoProcessor commandDtoProcessor =
-                metaModelService.commandDtoProcessorFor(commandDto.getMember().getLogicalMemberIdentifier());
-        if (commandDtoProcessor == null) {
-            return commandDto;
-        }
-        return commandDtoProcessor.process(commandWithDto, commandDto);
-    }
-
-
-    /**
-     * Uses the SPI infrastructure to copy over standard properties from {@link Command} to {@link CommandDto}.
-     */
-    @Service
-    @Named("isisApplib.ContentMappingServiceForCommandDto.CopyOverFromCommand")
-    // specify quite a high priority since custom processors will probably want to run after this one
-    // (but can choose to run before if they wish)
-    @Order(OrderPrecedence.EARLY)
-    @Qualifier("Command")
-    public static class CopyOverFromCommand implements CommandDtoProcessorService {
-
-        @Override
-        public CommandDto process(final Command command, CommandDto commandDto) {
-
-            // for some reason this isn't being persisted initially, so patch it in.  TODO: should fix this
-            commandDto.setUser(command.getUser());
-
-            // the timestamp field was only introduced in v1.4 of cmd.xsd, so there's no guarantee
-            // it will have been populated.  We therefore copy the value in from CommandWithDto entity.
-            if(commandDto.getTimestamp() == null) {
-                final Timestamp timestamp = command.getTimestamp();
-                commandDto.setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(timestamp));
-            }
-
-            CommandDtoUtils.setUserData(commandDto,
-                    CommandWithDto.USERDATA_KEY_TARGET_CLASS, command.getTargetClass());
-            CommandDtoUtils.setUserData(commandDto,
-                    CommandWithDto.USERDATA_KEY_TARGET_ACTION, command.getTargetAction());
-            CommandDtoUtils.setUserData(commandDto,
-                    CommandWithDto.USERDATA_KEY_ARGUMENTS, command.getArguments());
-
-            final Bookmark result = command.getResult();
-            CommandDtoUtils.setUserData(commandDto,
-                    CommandWithDto.USERDATA_KEY_RETURN_VALUE, result != null ? result.toString() : null);
-            // knowing whether there was an exception is on the master is used to determine whether to
-            // continue when replayed on the slave if an exception occurs there also
-            CommandDtoUtils.setUserData(commandDto,
-                    CommandWithDto.USERDATA_KEY_EXCEPTION, command.getException());
-
-            PeriodDto timings = CommandDtoUtils.timingsFor(commandDto);
-            timings.setStartedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(command.getStartedAt()));
-            timings.setCompletedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(command.getCompletedAt()));
-
-            return commandDto;
-        }
-    }
-
-
-    @Inject
-    MetaModelService metaModelService;
-
-    @Inject
-    List<CommandDtoProcessorService> commandDtoProcessorServices;
-
-}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/iactn/Interaction.java b/api/applib/src/main/java/org/apache/isis/applib/services/iactn/Interaction.java
index 9260b9a..3e2de34 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/iactn/Interaction.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/iactn/Interaction.java
@@ -50,7 +50,6 @@ import org.apache.isis.schema.ixn.v2.ObjectCountsDto;
 import org.apache.isis.schema.ixn.v2.PropertyEditDto;
 
 import lombok.Getter;
-import lombok.Setter;
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
@@ -83,8 +82,17 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 public class Interaction implements HasUniqueId {
 
-    @Getter @Setter
-    private UUID uniqueId;                          // <.>
+    public Interaction(final Command command) {
+        this.command = command;
+    }
+
+    @Getter
+    private Command command;                        // <.>
+
+    @Override
+    public UUID getUniqueId() {                     // <.>
+        return command.getUniqueId();
+    }
 
     // end::refguide[]
     private final List<Execution<?,?>> executionGraphs = _Lists.newArrayList();
@@ -159,16 +167,31 @@ public class Interaction implements HasUniqueId {
      * Use the provided {@link MemberExecutor} to invoke an action, with the provided
      * {@link ActionInvocation} capturing the details of said action.
      * </p>
+     *
+     * <p>
+     *     Because this both pushes an {@link Interaction.Execution} to
+     *     represent the action invocation and then pops it, that completed
+     *     execution is accessible at {@link Interaction#getPriorExecution()}.
+     * </p>
      */
     public Object execute(
             final MemberExecutor<ActionInvocation> memberExecutor,
             final ActionInvocation actionInvocation,
             final ClockService clockService,
-            final MetricsService metricsService) {
+            final MetricsService metricsService,
+            final Command command) {
 
-        push(actionInvocation);
+        pushAndStart(actionInvocation, clockService, metricsService, command);
+        try {
+            return executeInternal(memberExecutor, actionInvocation);
+        } finally {
+            popAndComplete(clockService, metricsService);
+        }
+    }
 
-        return executeInternal(memberExecutor, actionInvocation, clockService, metricsService);
+    private void pushAndStart(ActionInvocation actionInvocation, ClockService clockService, MetricsService metricsService, Command command) {
+        push(actionInvocation);
+        start(actionInvocation, clockService, metricsService, command);
     }
 
     /**
@@ -178,51 +201,50 @@ public class Interaction implements HasUniqueId {
      * Use the provided {@link MemberExecutor} to edit a property, with the provided
      * {@link PropertyEdit} capturing the details of said property edit.
      * </p>
+     *
+     * <p>
+     *     Because this both pushes an {@link Interaction.Execution} to
+     *     represent the property edit and then pops it, that completed
+     *     execution is accessible at {@link Interaction#getPriorExecution()}.
+     * </p>
      */
     public Object execute(
             final MemberExecutor<PropertyEdit> memberExecutor,
             final PropertyEdit propertyEdit,
             final ClockService clockService,
-            final MetricsService metricsService) {
+            final MetricsService metricsService,
+            final Command command) {
 
         push(propertyEdit);
-
-        return executeInternal(memberExecutor, propertyEdit, clockService, metricsService);
+        start(propertyEdit, clockService, metricsService, command);
+        try {
+            return executeInternal(memberExecutor, propertyEdit);
+        } finally {
+            popAndComplete(clockService, metricsService);
+        }
     }
 
-    private <T extends Execution<?,?>> Object executeInternal(
-            final MemberExecutor<T> memberExecutor,
-            final T execution,
-            final ClockService clockService,
-            final MetricsService metricsService) {
-
-        // as a convenience, since in all cases we want the command to start when the first 
-        // interaction executes, we populate the command here.
+    private <T extends Execution<?,?>> Object executeInternal(MemberExecutor<T> memberExecutor, T execution) {
 
         try {
-            try {
-                Object result = memberExecutor.execute(execution);
-                execution.setReturned(result);
-                return result;
-            } catch (Exception ex) {
-
-                //TODO there is an issue with exceptions getting swallowed, unless this is fixed,
-                // we rather print all of them, no matter whether recognized or not later on
-                // examples are IllegalArgument- or NullPointer- exceptions being swallowed when using the
-                // WrapperFactory utilizing async calls
-                log.error("failed to execute an interaction", ex);
-
-                // just because an exception has thrown, does not mean it is that significant;
-                // it could be that it is recognized by an ExceptionRecognizer and is not severe
-                // eg. unique index violation in the DB
-                getCurrentExecution().setThrew(ex);
-
-                // propagate (as in previous design); caller will need to trap and decide
-                throw ex;
-            }
-        } finally {
-            final Timestamp completedAt = clockService.nowAsJavaSqlTimestamp();
-            pop(completedAt, metricsService);
+            Object result = memberExecutor.execute(execution);
+            execution.setReturned(result);
+            return result;
+        } catch (Exception ex) {
+
+            //TODO there is an issue with exceptions getting swallowed, unless this is fixed,
+            // we rather print all of them, no matter whether recognized or not later on
+            // examples are IllegalArgument- or NullPointer- exceptions being swallowed when using the
+            // WrapperFactory utilizing async calls
+            log.error("failed to execute an interaction", ex);
+
+            // just because an exception has thrown, does not mean it is that significant;
+            // it could be that it is recognized by an ExceptionRecognizer and is not severe
+            // eg. unique index violation in the DB
+            getCurrentExecution().setThrew(ex);
+
+            // propagate (as in previous design); caller will need to trap and decide
+            throw ex;
         }
     }
 
@@ -253,6 +275,20 @@ public class Interaction implements HasUniqueId {
         return execution;
     }
 
+    private void start(
+            final Interaction.Execution<?,?> execution,
+            final ClockService clockService,
+            final MetricsService metricsService,
+            final Command command) {
+        // set the startedAt (and update command if this is the top-most member execution)
+        // (this isn't done within Interaction#execute(...) because it requires the DTO
+        // to have been set on the current execution).
+        val startedAt = execution.start(clockService, metricsService);
+        if(command.getStartedAt() == null) {
+            command.updater().setStartedAt(startedAt);
+        }
+    }
+
     /**
      * <b>NOT API</b>: intended to be called only by the framework.
      *
@@ -261,13 +297,16 @@ public class Interaction implements HasUniqueId {
      * from the stack of events held by the command.
      * </p>
      */
-    private Execution<?,?> pop(
-            final Timestamp completedAt,
+    private Execution<?,?> popAndComplete(
+            final ClockService clockService,
             final MetricsService metricsService) {
+
         if(currentExecution == null) {
             throw new IllegalStateException("No current execution to pop");
         }
         final Execution<?,?> popped = currentExecution;
+
+        final Timestamp completedAt = clockService.nowAsJavaSqlTimestamp();
         popped.setCompletedAt(completedAt, metricsService);
 
         moveCurrentTo(currentExecution.getParent());
@@ -386,8 +425,18 @@ public class Interaction implements HasUniqueId {
         // tag::refguide-2[]
         @Getter
         private final String targetMember;
-
         // end::refguide-2[]
+
+
+        /**
+         * Captures metrics before the Execution Dto is present.
+         */
+        private int numberObjectsLoadedBefore;
+        /**
+         * Captures metrics before the Execution Dto is present.
+         */
+        private int numberObjectsDirtiedBefore;
+
         protected Execution(
                 final Interaction interaction,
                 final InteractionType interactionType,
@@ -575,15 +624,8 @@ public class Interaction implements HasUniqueId {
                         final int numberObjectsDirtied) {
 
                     execution.startedAt = timestamp;
-
-                    final MetricsDto metricsDto = metricsFor(execution);
-
-                    final PeriodDto periodDto = timingsFor(metricsDto);
-                    periodDto.setStartedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(timestamp));
-
-                    final ObjectCountsDto objectCountsDto = objectCountsFor(metricsDto);
-                    numberObjectsLoadedFor(objectCountsDto).setBefore(numberObjectsLoaded);
-                    numberObjectsDirtiedFor(objectCountsDto).setBefore(numberObjectsDirtied);
+                    execution.numberObjectsLoadedBefore = numberObjectsLoaded;
+                    execution.numberObjectsDirtiedBefore = numberObjectsLoaded;
                 }
 
                 // tag::refguide-2a[]
@@ -603,9 +645,13 @@ public class Interaction implements HasUniqueId {
                     final MetricsDto metricsDto = metricsFor(execution);
 
                     final PeriodDto periodDto = timingsFor(metricsDto);
-                    periodDto.setCompletedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(timestamp));
+                    periodDto.setStartedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(execution.startedAt));
+                    periodDto.setCompletedAt(JavaSqlXMLGregorianCalendarMarshalling.toXMLGregorianCalendar(execution.completedAt));
 
                     final ObjectCountsDto objectCountsDto = objectCountsFor(metricsDto);
+                    numberObjectsLoadedFor(objectCountsDto).setBefore(execution.numberObjectsLoadedBefore);
+                    numberObjectsDirtiedFor(objectCountsDto).setBefore(execution.numberObjectsDirtiedBefore);
+
                     numberObjectsLoadedFor(objectCountsDto).setAfter(numberObjectsLoaded);
                     numberObjectsDirtiedFor(objectCountsDto).setAfter(numberObjectsDirtied);
                 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/iactn/InteractionContext.java b/api/applib/src/main/java/org/apache/isis/applib/services/iactn/InteractionContext.java
index 86cb93c..de41f1e 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/iactn/InteractionContext.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/iactn/InteractionContext.java
@@ -18,8 +18,10 @@
  */
 package org.apache.isis.applib.services.iactn;
 
+import javax.inject.Inject;
 import javax.inject.Named;
 
+import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Primary;
 import org.springframework.core.annotation.Order;
@@ -28,8 +30,15 @@ import org.springframework.stereotype.Service;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.IsisInteractionScope;
 import org.apache.isis.applib.annotation.OrderPrecedence;
+import org.apache.isis.applib.services.TransactionScopeListener;
+import org.apache.isis.applib.services.command.Command;
+import org.apache.isis.applib.services.inject.ServiceInjector;
+import org.apache.isis.applib.services.metrics.MetricsService;
 
 import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.val;
+import lombok.extern.log4j.Log4j2;
 
 /**
  * This service (API and implementation) provides access to context information about any {@link Interaction}.
@@ -41,14 +50,18 @@ import lombok.Getter;
 // tag::refguide[]
 @Service
 @Named("isisApplib.InteractionContext")
-@Order(OrderPrecedence.MIDPOINT)
+@Order(OrderPrecedence.EARLY - 10) // before ChangedObjectService
 @Primary
 @Qualifier("Default")
 @IsisInteractionScope
-//@Log4j2
-public class InteractionContext {
+@RequiredArgsConstructor(onConstructor_ = {@Inject})
+@Log4j2
+public class InteractionContext implements TransactionScopeListener, DisposableBean {
 
     // end::refguide[]
+
+    private final MetricsService metricsService;
+
     /**
      * The currently active {@link Interaction} for this thread.
      */
@@ -65,5 +78,19 @@ public class InteractionContext {
     }
 
     // tag::refguide[]
+
+    @Override
+    public void onTransactionEnded() {
+        val command = getInteraction().getCommand();
+        command.updater().setSystemStateChanged(
+                command.isSystemStateChanged() ||
+                        metricsService.numberObjectsDirtied() > 0);
+    }
+
+    @Override
+    public void destroy() throws Exception {
+        setInteraction(null);
+    }
+
 }
 // end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelService.java b/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelService.java
index ac5770a..840ea50 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/metamodel/MetaModelService.java
@@ -26,7 +26,7 @@ import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties.S
 
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.command.CommandDtoProcessor;
+import org.apache.isis.applib.services.commanddto.processor.CommandDtoProcessor;
 import org.apache.isis.commons.internal.collections._Sets;
 import org.apache.isis.schema.metamodel.v2.MetamodelDto;
 
@@ -79,7 +79,7 @@ public interface MetaModelService {
     BeanSort sortOf(Bookmark bookmark, Mode mode);      // <.>
 
     CommandDtoProcessor commandDtoProcessorFor(         // <.>
-                            String memberIdentifier);
+                            String logicalMemberIdentifier);
 
     // end::refguide[]
     // tag::refguide-1[]
@@ -92,7 +92,7 @@ public interface MetaModelService {
         STRICT,
         // end::refguide-1[]
         /**
-         * If the {@link #sortOf(Class, Mode) sort of} object type is unknown, then return {@link Sort#UNKNOWN}.
+         * If the {@link #sortOf(Class, Mode) sort of} object type is unknown, then return {@link BeanSort#UNKNOWN}.
          */
         // tag::refguide-1[]
         RELAXED
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCache.java b/api/applib/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCache.java
index 6e22285..c0e6a75 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCache.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/queryresultscache/QueryResultsCache.java
@@ -21,6 +21,8 @@ package org.apache.isis.applib.services.queryresultscache;
 import java.util.Arrays;
 import java.util.concurrent.Callable;
 
+import org.springframework.beans.factory.DisposableBean;
+
 import org.apache.isis.applib.services.MethodReferences;
 
 import lombok.Data;
@@ -38,7 +40,7 @@ import lombok.Getter;
  * available for use; no further configuration is required.
  */
 // tag::refguide[]
-public interface QueryResultsCache {
+public interface QueryResultsCache extends DisposableBean {
 
     <T> T execute(                                      // <.>
             Callable<T> callable,
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/scratchpad/Scratchpad.java b/api/applib/src/main/java/org/apache/isis/applib/services/scratchpad/Scratchpad.java
index 8f241ad..366698e 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/scratchpad/Scratchpad.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/scratchpad/Scratchpad.java
@@ -24,6 +24,7 @@ import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 import javax.inject.Named;
 
+import org.springframework.beans.factory.DisposableBean;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.annotation.Primary;
 import org.springframework.core.annotation.Order;
@@ -46,7 +47,8 @@ import lombok.extern.log4j.Log4j2;
  * available for use; no further configuration is required.
  */
 // tag::refguide[]
-public interface Scratchpad {
+public interface Scratchpad
+        extends DisposableBean {
 
     // end::refguide[]
     /**
@@ -62,12 +64,6 @@ public interface Scratchpad {
     // tag::refguide[]
     public void put(Object key, Object value);
 
-    // end::refguide[]
-    /**
-     * Clear any user data.
-     */
-    // tag::refguide[]
-    public void clear();
 
 }
 // end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/sudo/SudoService.java b/api/applib/src/main/java/org/apache/isis/applib/services/sudo/SudoService.java
index f5e94e2..621828b 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/sudo/SudoService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/sudo/SudoService.java
@@ -20,9 +20,8 @@
 package org.apache.isis.applib.services.sudo;
 
 import java.util.List;
-import java.util.concurrent.Callable;
+import java.util.function.Supplier;
 
-import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.applib.services.user.UserService;
 
 /**
@@ -65,7 +64,7 @@ public interface SudoService {
     // tag::refguide[]
     <T> T sudo(                                             // <.>
             String username,
-            final Callable<T> callable);
+            final Supplier<T> supplier);
 
     // end::refguide[]
     /**
@@ -83,7 +82,7 @@ public interface SudoService {
     // tag::refguide[]
     <T> T sudo(                                             // <.>
             String username, List<String> roles,
-            final Callable<T> callable);
+            final Supplier<T> supplier);
 
     // end::refguide[]
 
@@ -96,7 +95,7 @@ public interface SudoService {
         // end::refguide-1[]
         /**
          * Any implementation of the {@link SudoService} should call this method on all implementations of the
-         * {@link Spi} service whenever {@link SudoService#sudo(String, List, Callable)} (or its overloads)
+         * {@link Spi} service whenever {@link SudoService#sudo(String, List, Supplier)} (or its overloads)
          * is called.
          *
          * <p>
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/tablecol/TableColumnOrderForCollectionTypeAbstract.java b/api/applib/src/main/java/org/apache/isis/applib/services/tablecol/TableColumnOrderForCollectionTypeAbstract.java
new file mode 100644
index 0000000..33bc35d
--- /dev/null
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/tablecol/TableColumnOrderForCollectionTypeAbstract.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.apache.isis.applib.services.tablecol;
+
+import java.util.List;
+
+import lombok.RequiredArgsConstructor;
+
+// tag::refguide[]
+@RequiredArgsConstructor
+public abstract class TableColumnOrderForCollectionTypeAbstract<T>
+        implements TableColumnOrderService {
+
+    private final Class<T> collectionType;
+
+    public final List<String> orderParented(
+            final Object parent,
+            final String collectionId,
+            final Class<?> collectionType,
+            final List<String> propertyIds) {
+        if (! this.collectionType.isAssignableFrom(collectionType)) {
+            return propertyIds;
+        }
+        return orderParented(parent, collectionId, propertyIds);
+    }
+
+    protected List<String> orderParented(
+            final Object parent,
+            final String collectionId,
+            final List<String> propertyIds) {
+        return propertyIds;
+    }
+
+    public final List<String> orderStandalone(
+            final Class<?> collectionType,
+            final List<String> propertyIds) {
+        if (! this.collectionType.isAssignableFrom(collectionType)) {
+            return propertyIds;
+        }
+        return orderStandalone(propertyIds);
+    }
+
+    protected List<String> orderStandalone(
+            final List<String> propertyIds) {
+        return propertyIds;
+    }
+
+}
+// end::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java
index d5fccec..fa6ab86 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControl.java
@@ -39,14 +39,18 @@ import lombok.extern.log4j.Log4j2;
 @Log4j2
 public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
 
-    public static AsyncControl<Void> control() {                        // <.>
-        return new AsyncControl<>();
+    public static AsyncControl<Void> returningVoid() {                        // <.>
+        return new AsyncControl<>(Void.class);
     }
-    public static <X> AsyncControl<X> control(final Class<X> clazz) {   // <.>
-        return new AsyncControl<>();
+    public static <X> AsyncControl<X> returning(final Class<X> cls) {     // <.>
+        return new AsyncControl<X>(cls);
     }
 
-    private AsyncControl() {
+    @Getter
+    private final Class<R> returnType;                                  // <.>
+
+    private AsyncControl(final Class<R> returnType) {
+        this.returnType = returnType;
         with(exception -> {                                             // <.>
             log.error(logMessage(), exception);
             return null;
@@ -107,9 +111,8 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
      * Contains the result of the invocation.  However, if an entity is returned, then the object is automatically
      * detached because the persistence session within which it was obtained will have been closed already.
      */
-    @Setter(AccessLevel.PACKAGE)
     // tag::refguide[]
-    @Getter
+    @Getter @Setter
     private Future<R> future;                                           // <.>
 
     // end::refguide[]
@@ -127,6 +130,7 @@ public class AsyncControl<R> extends ControlAbstract<AsyncControl<R>> {
         }
         return buf.toString();
     }
+
     // tag::refguide[]
     // ...
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControlService.java b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControlService.java
deleted file mode 100644
index c112868..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/AsyncControlService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one
- *  or more contributor license agreements.  See the NOTICE file
- *  distributed with this work for additional information
- *  regarding copyright ownership.  The ASF licenses this file
- *  to you under the Apache License, Version 2.0 (the
- *  "License"); you may not use this file except in compliance
- *  with the License.  You may obtain a copy of the License at
- *
- *        http://www.apache.org/licenses/LICENSE-2.0
- *
- *  Unless required by applicable law or agreed to in writing,
- *  software distributed under the License is distributed on an
- *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- *  KIND, either express or implied.  See the License for the
- *  specific language governing permissions and limitations
- *  under the License.
- */
-package org.apache.isis.applib.services.wrapper.control;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.Future;
-
-import org.springframework.stereotype.Component;
-
-import org.apache.isis.applib.services.bookmark.Bookmark;
-
-import lombok.val;
-
-/**
- * For framework internal use.
- */
-@Component
-public class AsyncControlService {
-
-    public <R> AsyncControl<R> init(AsyncControl<R> asyncControl, Method method, Bookmark bookmark) {
-        asyncControl.setMethod(method);
-        asyncControl.setBookmark(bookmark);
-        return asyncControl;
-    }
-
-    public <R> AsyncControl<R> update(AsyncControl<R> asyncControl, Future<R> future) {
-        asyncControl.setFuture(future);
-        return asyncControl;
-    }
-
-    public <R> boolean shouldCheckRules(AsyncControl<R> asyncControl) {
-        val executionModes = asyncControl.getExecutionModes();
-        val skipRules = executionModes.contains(ExecutionMode.SKIP_RULE_VALIDATION);
-        return !skipRules;
-    }
-}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/ControlAbstract.java b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/ControlAbstract.java
index ca9e1c3..885c150 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/ControlAbstract.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/wrapper/control/ControlAbstract.java
@@ -38,17 +38,15 @@ public class ControlAbstract<T extends ControlAbstract<T>> {
     }
 
     /**
-     * Set by framework.
+     * Set by framework; simply used for logging purposes.
      */
-    @Setter(AccessLevel.PACKAGE)
-    @Getter(AccessLevel.PACKAGE)
+    @Getter(AccessLevel.PACKAGE) @Setter
     private Method method;
 
     /**
-     * Set by framework.
+     * Set by framework; simply used for logging purposes.
      */
-    @Setter(AccessLevel.PACKAGE)
-    @Getter(AccessLevel.PACKAGE)
+    @Getter(AccessLevel.PACKAGE) @Setter
     private Bookmark bookmark;
 
     // tag::refguide[]
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java b/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
index fb5d805..f308df4 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/xactn/TransactionService.java
@@ -85,23 +85,8 @@ public interface TransactionService {
      */
     // tag::refguide[]
     <T> T executeWithinTransaction(Supplier<T> task);       // <.>
-
     // end::refguide[]
 
-//   the executeWithinNewTransaction at time of writing is incorrect (doesn't create a new xactn).
-//   not sure there's any need for these additional methods?
-//
-//    /**
-//     * Runs given {@code task} within its own (new) transactional boundary.
-//     * @param task
-//     */
-//    void executeWithinNewTransaction(Runnable task);        // <.>
-//
-//    /**
-//     * Runs given {@code task} within its own (new) transactional boundary.
-//     * @param task
-//     */
-//    <T> T executeWithinNewTransaction(Supplier<T> task);    // <.>
 
     // tag::refguide[]
 }
diff --git a/api/applib/src/main/java/org/apache/isis/applib/util/ToString.java b/api/applib/src/main/java/org/apache/isis/applib/util/ToString.java
index 129c52d..6bcd50a 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/util/ToString.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/util/ToString.java
@@ -41,7 +41,7 @@ public class ToString<T> {
         return new ToString<>(name, getter, false);
     }
 
-    public static <T> ToString<T> toStringOmmitIfAbsent(String name, Function<? super T, ?> getter) {
+    public static <T> ToString<T> toStringOmitIfAbsent(String name, Function<? super T, ?> getter) {
         Objects.requireNonNull(name);
         Objects.requireNonNull(getter);
         return new ToString<>(name, getter, true);
@@ -49,10 +49,10 @@ public class ToString<T> {
 
     private final List<String> names = _Lists.newArrayList();
     private final List<Function<? super T, ?>> getters = _Lists.newArrayList();
-    private final BitSet ommitIfAbsent = new BitSet();
+    private final BitSet omitIfAbsent = new BitSet();
 
-    private ToString(String name, Function<? super T, ?> getter, boolean ommitIfAbsent) {
-        addBit(ommitIfAbsent);
+    private ToString(String name, Function<? super T, ?> getter, boolean omitIfAbsent) {
+        addBit(omitIfAbsent);
         names.add(name);
         getters.add(getter);
     }
@@ -66,7 +66,7 @@ public class ToString<T> {
         return this;
     }
 
-    public ToString<T> thenToStringOmmitIfAbsent(String name, Function<? super T, ?> getter){
+    public ToString<T> thenToStringOmitIfAbsent(String name, Function<? super T, ?> getter){
         Objects.requireNonNull(name);
         Objects.requireNonNull(getter);
         addBit(true);
@@ -95,13 +95,12 @@ public class ToString<T> {
 
         return String.format("%s{%s}",
 
-                //ommitIfAbsent.toString(),
                 target.getClass().getSimpleName(),
 
                 getters.stream()
                 .peek(__->index[0]++)
                 .map(getter->getter.apply(target))
-                .filter(value->value!=null || !ommitIfAbsent.get(index[0]))
+                .filter(value->value!=null || !omitIfAbsent.get(index[0]))
                 .map(valueToStringFunction)
                 .map(valueLiteral->names.get(index[0])+"="+valueLiteral)
                 .collect(Collectors.joining(", "))
@@ -114,9 +113,9 @@ public class ToString<T> {
     private void addBit(boolean bit) {
         final int index = names.size();
         if(bit) {
-            ommitIfAbsent.set(index);
+            omitIfAbsent.set(index);
         } else {
-            ommitIfAbsent.clear(index);
+            omitIfAbsent.clear(index);
         }
     }
 
diff --git a/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommandDtoUtils.java b/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommandDtoUtils.java
index b02c91c..ea5e56f 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommandDtoUtils.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/util/schema/CommandDtoUtils.java
@@ -30,7 +30,10 @@ import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
 import javax.xml.bind.Unmarshaller;
 
+import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.util.JaxbUtil;
+import org.apache.isis.commons.internal.base._Strings;
+import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.commons.internal.resources._Resources;
 import org.apache.isis.schema.cmd.v2.ActionDto;
 import org.apache.isis.schema.cmd.v2.CommandDto;
@@ -63,6 +66,10 @@ public final class CommandDtoUtils {
         }
     }
 
+    public static CommandDto clone(final CommandDto commandDto) {
+        return fromXml(toXml(commandDto));
+    }
+
     public static CommandDto fromXml(final String xml) {
         return fromXml(new StringReader(xml));
     }
@@ -130,13 +137,29 @@ public final class CommandDtoUtils {
 
     public static void setUserData(
             final CommandDto dto, final String key, final String value) {
-        if(dto == null || key == null) {
+        if(dto == null || key == null || _Strings.isNullOrEmpty(value)) {
             return;
         }
         final MapDto userData = userDataFor(dto);
         CommonDtoUtils.putMapKeyValue(userData, key, value);
     }
 
+    public static void setUserData(
+            final CommandDto dto, final String key, final Bookmark bookmark) {
+        if(dto == null || key == null || bookmark == null) {
+            return;
+        }
+        setUserData(dto, key, bookmark.toString());
+    }
+
+    public static void clearUserData(
+            final CommandDto dto, final String key) {
+        if(dto == null || key == null) {
+            return;
+        }
+        userDataFor(dto).getEntry().removeIf(x -> x.getKey().equals(key));
+    }
+
     private static MapDto userDataFor(final CommandDto commandDto) {
         MapDto userData = commandDto.getUserData();
         if(userData == null) {
diff --git a/api/applib/src/test/java/org/apache/isis/applib/services/interaction/InteractionDefaultTest_next.java b/api/applib/src/test/java/org/apache/isis/applib/services/interaction/InteractionDefaultTest_next.java
index 5e01d85..0260066 100644
--- a/api/applib/src/test/java/org/apache/isis/applib/services/interaction/InteractionDefaultTest_next.java
+++ b/api/applib/src/test/java/org/apache/isis/applib/services/interaction/InteractionDefaultTest_next.java
@@ -30,7 +30,7 @@ public class InteractionDefaultTest_next {
 
     @Test
     public void test() {
-        final Interaction interaction = new Interaction();
+        final Interaction interaction = new Interaction(null);
 
         assertThat(interaction.next("foo"), is(0));
         assertThat(interaction.next("foo"), is(1));
diff --git a/api/applib/src/test/java/org/apache/isis/applib/services/wrapper/control/AsyncControl_Test.java b/api/applib/src/test/java/org/apache/isis/applib/services/wrapper/control/AsyncControl_Test.java
index 3846167..52e9175 100644
--- a/api/applib/src/test/java/org/apache/isis/applib/services/wrapper/control/AsyncControl_Test.java
+++ b/api/applib/src/test/java/org/apache/isis/applib/services/wrapper/control/AsyncControl_Test.java
@@ -19,7 +19,6 @@
 package org.apache.isis.applib.services.wrapper.control;
 
 import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
 
 import org.assertj.core.api.Assertions;
 import org.junit.Test;
@@ -34,7 +33,7 @@ public class AsyncControl_Test {
     public void defaults() throws Exception {
 
         // given
-        val control = AsyncControl.control();
+        val control = AsyncControl.returningVoid();
 
         // then
         Assertions.assertThat(control.getExecutionModes()).isEmpty();
@@ -43,7 +42,7 @@ public class AsyncControl_Test {
     @Test
     public void check_rules() throws Exception {
         // given
-        val control = AsyncControl.control();
+        val control = AsyncControl.returningVoid();
 
         // when
         control.withCheckRules();
@@ -56,7 +55,7 @@ public class AsyncControl_Test {
     public void skip_rules() throws Exception {
 
         // given
-        val control = AsyncControl.control();
+        val control = AsyncControl.returningVoid();
 
         // when
         control.withSkipRules();
@@ -69,7 +68,7 @@ public class AsyncControl_Test {
     public void user() throws Exception {
 
         // given
-        val control = AsyncControl.control();
+        val control = AsyncControl.returningVoid();
 
         // when
         control.withUser("fred");
@@ -82,7 +81,7 @@ public class AsyncControl_Test {
     public void roles() throws Exception {
 
         // given
-        val control = AsyncControl.control();
+        val control = AsyncControl.returningVoid();
 
         // when
         control.withRoles("role-1", "role-2");
@@ -101,7 +100,7 @@ public class AsyncControl_Test {
         }));
         ExceptionHandler exceptionHandler = ex -> null;
 
-        val control = AsyncControl.control(String.class)
+        val control = AsyncControl.returning(String.class)
                 .withSkipRules()
                 .withUser("fred")
                 .withRoles("role-1", "role-2")
diff --git a/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommandDtoUtils_Test.java b/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommandDtoUtils_Test.java
index 9e3cc5f..d4e84c3 100644
--- a/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommandDtoUtils_Test.java
+++ b/api/applib/src/test/java/org/apache/isis/applib/util/schema/CommandDtoUtils_Test.java
@@ -62,7 +62,18 @@ public class CommandDtoUtils_Test {
         CommandDtoUtils.setUserData(dto, "someKey", "someOtherValue");
         assertThat(CommandDtoUtils.getUserData(dto, "someKey"), is("someOtherValue"));
 
-        CommandDtoUtils.setUserData(dto, "someKey", null);
+    }
+
+    @Test
+    public void clearUserData() {
+        // given
+        CommandDtoUtils.setUserData(dto, "someKey", "someOtherValue");
+        assertThat(CommandDtoUtils.getUserData(dto, "someKey"), is("someOtherValue"));
+
+        // when
+        CommandDtoUtils.clearUserData(dto, "someKey");
+
+        // then
         assertThat(CommandDtoUtils.getUserData(dto, "someKey"), is(nullValue()));
     }
 }
\ No newline at end of file
diff --git a/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java b/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java
index b974944..51c5d07 100644
--- a/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java
+++ b/commons/src/main/java/org/apache/isis/commons/internal/exceptions/_Exceptions.java
@@ -27,6 +27,7 @@ import java.util.Optional;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
+import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import javax.annotation.Nullable;
@@ -270,6 +271,19 @@ public final class _Exceptions {
                 .limit(maxLines);
     }
 
+    public static final String asStacktrace(@Nullable Throwable ex, int maxLines, String delimiter) {
+        return _Exceptions.streamStacktraceLines(ex, maxLines)
+                .collect(Collectors.joining(delimiter));
+    }
+
+    public static final String asStacktrace(@Nullable Throwable ex, int maxLines) {
+        return asStacktrace(ex, maxLines, "\n");
+    }
+
+    public static final String asStacktrace(@Nullable Throwable ex) {
+        return asStacktrace(ex, 1000);
+    }
+
     /**
      * Dumps the current thread's stack-trace onto the given {@code writer}.
      * @param writer
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/Other.adoc b/core/config/src/main/adoc/modules/config/pages/sections/Other.adoc
index 8d2f543..369e82c 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/Other.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/Other.adoc
@@ -15,7 +15,7 @@ include::../section-hooks/Other~pre.adoc[]
 [[isis.as-map]]
 isis.as-map
 
-|
+| 
 | null
 
 
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/_nav.adoc b/core/config/src/main/adoc/modules/config/pages/sections/_nav.adoc
index 50051f8..fa17556 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/_nav.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/_nav.adoc
@@ -2,7 +2,7 @@
 ** xref:refguide:config:sections/isis.core.meta-model.adoc[Core MetaModel]
 ** xref:refguide:config:sections/isis.core.meta-model.introspector.adoc[Core MetaModel Introspection]
 ** xref:refguide:config:sections/isis.core.meta-model.validator.adoc[MetaModel Validator]
-** xref:refguide:config:sections/isis.core.runtime.adoc[Core Runtime Services configurations]
+** xref:refguide:config:sections/isis.core.runtime.adoc[Core Runtime configurations]
 ** xref:refguide:config:sections/isis.core.runtime-services.adoc[Core Runtime Services configurations]
 ** xref:refguide:config:sections/isis.security.shiro.adoc[Shiro Security Implementation]
 ** xref:refguide:config:sections/isis.persistence.jdo-datanucleus.adoc[JDO DataNucleus]
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.applib.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.applib.adoc
index 34d6d06..28b5b43 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.applib.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.applib.adoc
@@ -17,7 +17,7 @@ isis.applib.annotation. +
 action-layout.css-class-fa. +
 patterns
 
-|
+| 
 | 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.
 
 Providing a default set of patterns encourages a common set of verbs to be used.
@@ -30,7 +30,7 @@ The font awesome class for individual actions can be overridden using ``ActionLa
 isis.applib.annotation. +
 action-layout.css-class.patterns
 
-|
+| 
 | 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.
 
 Providing a default set of patterns encourages a common set of verbs to be used.
@@ -43,10 +43,8 @@ The CSS class for individual actions can be overridden using ``ActionLayout#cssC
 isis.applib.annotation.action. +
 command
 
-|
-| The default for whether action invocations should be reified as a ``Command`` using the ``CommandService``, possibly so that the actual execution of the action can be deferred until later (background execution) or replayed against a copy of the system.
-
-In particular, the ``CommandWithDto`` implementation of ``Command`` represents the action invocation memento (obtained using ``CommandWithDto#asDto()``) as a ``CommandDto``.
+| 
+| The default for whether action invocations should be reified as a ``Command`` using the ``CommandService``, either for auditing or for replayed against a secondary system, eg for regression testing.
 
 This setting can be overridden on a case-by-case basis using ``Action#command()``.
 
@@ -73,7 +71,7 @@ The algorithm for determining whether (and what type of) an event is actually se
 isis.applib.annotation.action. +
 explicit
 
-|
+| 
 | Whether or not a public method needs to be annotated with @``Action`` in order to be picked up as an action in the metamodel.
 
 
@@ -82,7 +80,7 @@ explicit
 isis.applib.annotation.action. +
 publishing
 
-|
+| 
 | The default for whether action invocations should be sent through to the ``PublisherService`` for publishing.
 
 The service's publish method is called only once per transaction, with ``Execution`` collecting details of the identity of the target object, the action invoked, the action arguments and the returned object (if any).
@@ -95,7 +93,7 @@ This setting can be overridden on a case-by-case basis using ``Action#publishing
 isis.applib.annotation. +
 collection-layout.default-view
 
-|
+| 
 | Defines the initial view to display collections when rendered.
 
 The value of this can be overridden on a case-by-case basis using ``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
@@ -136,7 +134,7 @@ domain-object-layout. +
 css-class-ui-event. +
 post-for-default
 
-|  true
+| 
 | Influences whether an ``CssClassUiEvent`` should be published (on the internal ``EventBusService``) whenever a domain object is about to be rendered in the UI - thereby allowing subscribers to optionally ``CssClassUiEvent#setCssClass(String)`` change) the CSS classes that are used.
 
 The algorithm for determining whether (and what type of) an event is sent depends on the value of the ``DomainObjectLayout#cssClassUiEvent()`` @DomainObjectLayout(cssClassEvent=...)} for the domain object in question.
@@ -145,6 +143,8 @@ The algorithm for determining whether (and what type of) an event is sent depend
 * If set to some subtype of CssClassUiEvent.Default, then an event is sent _if and only if_ this configuration setting is set.
 * If set to any other subtype, then an event _is_ sent.
 
+The default is ``false``, because otherwise the mere presence of ``@DomainObjectLayout`` (perhaps for some attribute other than this one) will cause any imperative ``cssClass()`` method to be ignored.
+
 
 |
 [[isis.applib.annotation.domain-object-layout.icon-ui-event.post-for-default]]
@@ -152,7 +152,7 @@ isis.applib.annotation. +
 domain-object-layout.icon-ui-event. +
 post-for-default
 
-|  true
+| 
 | Influences whether an ``IconUiEvent`` should be published (on the internal ``EventBusService``) whenever a domain object is about to be rendered in the UI - thereby allowing subscribers to optionally ``IconUiEvent#setIconName(String)`` change) the icon that is used.
 
 The algorithm for determining whether (and what type of) an event is sent depends on the value of the ``DomainObjectLayout#iconUiEvent()`` @DomainObjectLayout(iconEvent=...)} for the domain object in question.
@@ -161,6 +161,8 @@ The algorithm for determining whether (and what type of) an event is sent depend
 * If set to some subtype of IconUiEvent.Default, then an event is sent _if and only if_ this configuration setting is set.
 * If set to any other subtype, then an event _is_ sent.
 
+The default is ``false``, because otherwise the mere presence of ``@DomainObjectLayout`` (perhaps for some attribute other than this one) will cause any imperative ``iconName()`` method to be ignored.
+
 
 |
 [[isis.applib.annotation.domain-object-layout.layout-ui-event.post-for-default]]
@@ -168,7 +170,7 @@ isis.applib.annotation. +
 domain-object-layout. +
 layout-ui-event.post-for-default
 
-|  true
+| 
 | Influences whether an ``LayoutUiEvent`` should be published (on the internal ``EventBusService``) whenever a domain object is about to be rendered in the UI - thereby allowing subscribers to optionally ``LayoutUiEvent#setLayout(String)`` change) the layout that is used.
 
 If a different layout value has been set, then a layout in the form ``xml`` use used (where ``zzz`` is the name of the layout).
@@ -179,6 +181,8 @@ The algorithm for determining whether (and what type of) an event is sent depend
 * If set to some subtype of LayoutUiEvent.Default, then an event is sent _if and only if_ this configuration setting is set.
 * If set to any other subtype, then an event _is_ sent.
 
+The default is ``false``, because otherwise the mere presence of ``@DomainObjectLayout`` (perhaps for some attribute other than this one) will cause any imperative ``layout()`` method to be ignored.
+
 
 |
 [[isis.applib.annotation.domain-object-layout.paged]]
@@ -197,7 +201,7 @@ isis.applib.annotation. +
 domain-object-layout. +
 title-ui-event.post-for-default
 
-|  true
+| 
 | Influences whether an ``TitleUiEvent`` should be published (on the internal ``EventBusService``) whenever a domain object is about to be rendered in the UI - thereby allowing subscribers to optionally ``TitleUiEvent#setTitle(String)`` change) the title that is used.
 
 The algorithm for determining whether (and what type of) an event is sent depends on the value of the ``DomainObjectLayout#titleUiEvent()`` @DomainObjectLayout(titleEvent=...)} for the domain object in question.
@@ -206,13 +210,15 @@ The algorithm for determining whether (and what type of) an event is sent depend
 * If set to some subtype of TitleUiEvent.Default, then an event is sent _if and only if_ this configuration setting is set.
 * If set to any other subtype, then an event _is_ sent.
 
+The default is ``false``, because otherwise the mere presence of ``@DomainObjectLayout`` (perhaps for some attribute other than this one) will cause any imperative ``title()`` method to be ignored.
+
 
 |
 [[isis.applib.annotation.domain-object.auditing]]
 isis.applib.annotation. +
 domain-object.auditing
 
-|
+| 
 | The default for whether _domain entities_ should be audited or not (meaning that any changes are sent through to the ``AuditerService``.
 
 This setting can be overridden on a case-by-case basis using ``DomainObject#auditing()`` DomainObject#getAuditing()}
@@ -242,7 +248,7 @@ The algorithm for determining whether (and what type of) an event is sent depend
 isis.applib.annotation. +
 domain-object.editing
 
-|
+| 
 | 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).
 
 This setting can be overridden on a case-by-case basis using DomainObject#getEditing()
@@ -310,7 +316,7 @@ Note: this applies only to domain entities, not to view models.
 isis.applib.annotation. +
 domain-object.publishing
 
-|
+| 
 | The default for whether the identities of changed objects should be sent through to the ``PublisherService`` for publishing.
 
 The service's publish method is called only once per transaction, with ``PublishedObjects`` collecting details of all changed domain objects.
@@ -382,7 +388,7 @@ Note: this applies only to domain entities, not to view models.
 isis.applib.annotation. +
 parameter-layout.label-position
 
-|
+| 
 | Defines the default position for the label for an action parameter.
 
 Can be overridden on a case-by-case basis using ``ParameterLayout#labelPosition()``.
@@ -395,7 +401,7 @@ If left as ``LabelPosition#NOT_SPECIFIED`` and not overridden, then the position
 isis.applib.annotation. +
 property-layout.label-position
 
-|
+| 
 | Defines the default position for the label for a domain object property.
 
 Can be overridden on a case-by-case basis using ``ParameterLayout#labelPosition()``.
@@ -408,12 +414,10 @@ If left as ``LabelPosition#NOT_SPECIFIED`` and not overridden, then the position
 isis.applib.annotation.property. +
 command
 
-|
-| The default for whether property edits should be reified as a ``Command`` using the ``CommandService``, possibly so that the actual execution of the property edit can be deferred until later (background execution) or replayed against a copy of the system.
-
-In particular, the ``CommandWithDto`` implementation of ``Command`` represents the action invocation memento (obtained using ``CommandWithDto#asDto()``) as a ``CommandDto``.
+| 
+| The default for whether property edits should be reified as a ``Command`` using the ``CommandService``, for example for auditing, or to be replayed against a secondary system, for regression testing.
 
-This setting can be overridden on a case-by-case basis using ``Action#command()``.
+This setting can be overridden on a case-by-case basis using ``Property#command()``.
 
 
 |
@@ -438,7 +442,7 @@ The algorithm for determining whether (and what type of) an event is actually se
 isis.applib.annotation.property. +
 publishing
 
-|
+| 
 | The default for whether property edits should be sent through to the ``PublisherService`` for publishing.
 
 The service's publish method is called only once per transaction, with ``Execution`` collecting details of the identity of the target object, the property edited, and the new value of the property.
@@ -519,7 +523,7 @@ isis.applib.annotation.view-model. +
 validation.semantic-checking. +
 enable
 
-|
+| 
 | Whether to check for inconsistencies between the usage of ``DomainObject``, ``ViewModel``, ``DomainObjectLayout`` and ``ViewModelLayout``.
 
 
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.adoc
index f9aec4a..a194ad1 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.adoc
@@ -28,7 +28,7 @@ isis.core.meta-model. +
 programming-model. +
 ignore-deprecated
 
-|
+| 
 | If set, then any aspects of the programming model (as implemented by ``FacetFactory``s that have been indicated as deprecated will simply be ignored/excluded from the metamodel.
 
 
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.introspector.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.introspector.adoc
index e12efa6..d288d75 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.introspector.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.introspector.adoc
@@ -27,7 +27,7 @@ Only applies if the introspector is configured to perform full introspection up-
 isis.core.meta-model.introspector. +
 mode
 
-|
+| 
 | Whether all known types should be fully introspected as part of the bootstrapping, or should only be partially introspected initially.
 
 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 ``#isValidateIncrementally()`` is disabled.
@@ -38,8 +38,10 @@ Leaving this as lazy means that there's a chance that metamodel validation error
 isis.core.meta-model.introspector. +
 parallelize
 
-|  true
-| Whether to perform introspection in parallel.
+| 
+| Whether to perform introspection in parallel. Meant to speed up bootstrapping.
+
+For now this is _experimental_. Leave this disabled (the default).
 
 
 |
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.validator.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.validator.adoc
index 991ede3..34b4a40 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.validator.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.core.meta-model.validator.adoc
@@ -50,7 +50,7 @@ It is _highly advisable_ to leave this set as enabled (the default), and to also
 isis.core.meta-model.validator. +
 explicit-object-type
 
-|
+| 
 | Whether to ensure that the object type of all objects must be specified explicitly, using either ``DomainObject#objectType()`` or ``DomainService#objectType()``.
 
 It is _highly advisable_ to leave this set as enabled (the default). These object types should also (of course) be unique - that can be checked by setting the ``#isEnsureUniqueObjectTypes()`` config property.
@@ -71,7 +71,7 @@ date-time-type-adapter
 isis.core.meta-model.validator. +
 jaxb-view-model.no-arg-constructor
 
-|
+| 
 | If set, then ensures that all JAXB-style view models have a no-arg constructor.
 
 
@@ -126,7 +126,7 @@ jdoql.variables-clause
 isis.core.meta-model.validator. +
 no-params-only
 
-|
+| 
 | If set, then checks that the supports ``hideXxx`` and ``disableXxx`` methods for actions do not have take parameters.
 
 Historically, the programming model allowed these methods to accept the same number of parameters as the action method to which they relate, the rationale being for similarity with the ``validateXxx`` method. However, since these parameters serve no function, the programming model has been simplified so that these supporting methods are discovered if they have exactly no parameters.
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.core.runtime-services.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.core.runtime-services.adoc
index c330019..23be405 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.core.runtime-services.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.core.runtime-services.adoc
@@ -16,7 +16,7 @@ include::../section-hooks/isis.core.runtime-services~pre.adoc[]
 isis.core.runtime-services. +
 application-features.init
 
-|
+| 
 | Whether the ``ApplicationFeatureRepository`` (or the default implementation of that service, at least) should compute the set of ``ApplicationFeature`` that describe the metamodel eagerly, or lazily.
 
 
@@ -25,7 +25,7 @@ application-features.init
 isis.core.runtime-services.email. +
 override.bcc
 
-|
+| 
 | Intended for testing purposes only, if set then the requested ``bcc:`` of the email will be ignored, and instead sent to this email address instead.
 
 
@@ -34,7 +34,7 @@ override.bcc
 isis.core.runtime-services.email. +
 override.cc
 
-|
+| 
 | Intended for testing purposes only, if set then the requested ``cc:`` of the email will be ignored, and instead sent to this email address instead.
 
 
@@ -43,7 +43,7 @@ override.cc
 isis.core.runtime-services.email. +
 override.to
 
-|
+| 
 | Intended for testing purposes only, if set then the requested ``to:`` of the email will be ignored, and instead sent to this email address instead.
 
 
@@ -61,7 +61,7 @@ port
 isis.core.runtime-services.email. +
 sender.address
 
-|
+| 
 | Specifies the email address of the user sending the email.
 
 If the username is not specified, is also used as the username to connect to the SMTP service.
@@ -74,7 +74,7 @@ This configuration property is mandatory (for the default implementation of the
 isis.core.runtime-services.email. +
 sender.hostname
 
-|
+| 
 | Specifies the host running the SMTP service.
 
 If not specified, then the value used depends upon the email implementation. The default implementation will use the ``host`` system property.
@@ -85,7 +85,7 @@ If not specified, then the value used depends upon the email implementation. The
 isis.core.runtime-services.email. +
 sender.password
 
-|
+| 
 | Specifies the password (corresponding to the username to connect to the SMTP service.
 
 This configuration property is mandatory (for the default implementation of the ``EmailService``, at least).
@@ -96,7 +96,7 @@ This configuration property is mandatory (for the default implementation of the
 isis.core.runtime-services.email. +
 sender.username
 
-|
+| 
 | Specifies the username to use to connect to the SMTP service.
 
 If not specified, then the sender's email address will be used instead.
@@ -126,7 +126,7 @@ isis.core.runtime-services.email. +
 throw-exception-on-fail
 
 |  true
-| 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.isis.applib.services.email.EmailService#send(List, List, List, String, String, DataSource...)) that's being called.
+| 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 (List, List, String, String, DataSource...) that's being called.
 
 
 |
@@ -143,7 +143,7 @@ tls.enabled
 isis.core.runtime-services. +
 exception-recognizer.jdo.disable
 
-|
+| 
 | Whether the ``ExceptionRecognizer`` implementation for JDO/DataNucleus object store - which attempts to sanitize any exceptions arising from that object store - should be disabled (meaning that exceptions will potentially propagate as more serious to the end user).
 
 
@@ -153,7 +153,7 @@ isis.core.runtime-services. +
 repository-service. +
 disable-auto-flush
 
-|
+| 
 | Normally any queries are automatically preceded by flushing pending executions.
 
 This key allows this behaviour to be disabled.
@@ -166,7 +166,7 @@ Originally introduced as part of ISIS-1134 (fixing memory leaks in the objectsto
 isis.core.runtime-services. +
 translation.po.mode
 
-|
+| 
 | Specifies the initial mode for obtaining/discovering translations.
 
 There are three modes:
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.core.runtime.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.core.runtime.adoc
index ef61cd5..0d78569 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.core.runtime.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.core.runtime.adoc
@@ -15,7 +15,7 @@ include::../section-hooks/isis.core.runtime~pre.adoc[]
 [[isis.core.runtime.locale]]
 isis.core.runtime.locale
 
-|
+| 
 | If set, then overrides the application's ``Locale#getDefault()``
 
 
@@ -23,7 +23,7 @@ isis.core.runtime.locale
 [[isis.core.runtime.timezone]]
 isis.core.runtime.timezone
 
-|
+| 
 | If set, then override's the application's timezone.
 
 
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.extensions.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.extensions.adoc
index 3e55561..9f3426a 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.extensions.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.extensions.adoc
@@ -12,11 +12,117 @@ include::../section-hooks/isis.extensions~pre.adoc[]
 |Default
 |Description
 |
+[[isis.extensions.command-replay.primary.base-url]]
+isis.extensions.command-replay. +
+primary.base-url
+
+| 
+| null
+
+
+|
+[[isis.extensions.command-replay.primary.base-url-end-user]]
+isis.extensions.command-replay. +
+primary.base-url-end-user
+
+| 
+| null
+
+
+|
+[[isis.extensions.command-replay.primary.batch-size]]
+isis.extensions.command-replay. +
+primary.batch-size
+
+|  10
+| null
+
+
+|
+[[isis.extensions.command-replay.primary.password]]
+isis.extensions.command-replay. +
+primary.password
+
+| 
+| null
+
+
+|
+[[isis.extensions.command-replay.primary.user]]
+isis.extensions.command-replay. +
+primary.user
+
+| 
+| null
+
+
+|
+[[isis.extensions.command-replay.secondary.analyser.exception.enabled]]
+isis.extensions.command-replay. +
+secondary.analyser.exception. +
+enabled
+
+|  true
+| null
+
+
+|
+[[isis.extensions.command-replay.secondary.analyser.result.enabled]]
+isis.extensions.command-replay. +
+secondary.analyser.result.enabled
+
+|  true
+| null
+
+
+|
+[[isis.extensions.command-replay.secondary.quartz-replicate-and-replay-job.repeat-interval]]
+isis.extensions.command-replay. +
+secondary. +
+quartz-replicate-and-replay-job. +
+repeat-interval
+
+|  10000
+| Number of milliseconds before running again.
+
+
+|
+[[isis.extensions.command-replay.secondary.quartz-replicate-and-replay-job.start-delay]]
+isis.extensions.command-replay. +
+secondary. +
+quartz-replicate-and-replay-job. +
+start-delay
+
+|  15000
+| Number of milliseconds before starting the job.
+
+
+|
+[[isis.extensions.command-replay.secondary.quartz-session.roles]]
+isis.extensions.command-replay. +
+secondary.quartz-session.roles
+
+|  isisModuleExtCommandReplaySecondar +
+yRole
+| null
+
+
+|
+[[isis.extensions.command-replay.secondary.quartz-session.user]]
+isis.extensions.command-replay. +
+secondary.quartz-session.user
+
+|  isisModuleExtCommandReplaySecondar +
+yUser
+| The user that runs the replay session secondary.
+
+
+|
 [[isis.extensions.cors.allowed-headers]]
 isis.extensions.cors. +
 allowed-headers
 
-|
+| 
 | Which HTTP headers are allowed in a CORS request.
 
 For more information, check the usage of the ``headers`` init parameter for https://github.com/eBay/cors-filter[EBay CORSFilter].
@@ -27,7 +133,7 @@ For more information, check the usage of the ``headers`` init parameter for http
 isis.extensions.cors. +
 allowed-methods
 
-|
+| 
 | Which HTTP methods are permitted in a CORS request.
 
 For more information, check the usage of the ``methods`` init parameter for https://github.com/eBay/cors-filter[EBay CORSFilter].
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.legacy.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.legacy.adoc
index af36d67..72336de 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.legacy.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.legacy.adoc
@@ -16,7 +16,7 @@ include::../section-hooks/isis.legacy~pre.adoc[]
 isis.legacy.value-types.money. +
 currency
 
-|
+| 
 | Configures the default currency code used by ``MoneyValueSemanticsProvider``. @deprecated
 
 
@@ -25,7 +25,7 @@ currency
 isis.legacy.value-types.percentage. +
 format
 
-|
+| 
 | Configures the formats understood by ``PercentageValueSemanticsProvider``. @deprecated
 
 
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.persistence.jdo-datanucleus.impl.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.persistence.jdo-datanucleus.impl.adoc
index 95f99cc..986e28a 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.persistence.jdo-datanucleus.impl.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.persistence.jdo-datanucleus.impl.adoc
@@ -17,7 +17,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus. +
 ConnectionFactory2Name
 
-|
+| 
 | null
 
 
@@ -27,7 +27,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus. +
 ConnectionFactoryName
 
-|
+| 
 | null
 
 
@@ -37,7 +37,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus. +
 ConnectionPasswordDecrypter
 
-|
+| 
 | null
 
 
@@ -47,7 +47,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus. +
 PersistenceUnitLoadClasses
 
-|
+| 
 | null
 
 
@@ -56,7 +56,7 @@ PersistenceUnitLoadClasses
 isis.persistence.jdo-datanucleus. +
 impl.datanucleus.cache.level2.mode
 
-|
+| 
 | Values of javax.persistence.SharedCacheMode, capitalized
 
 
@@ -81,7 +81,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus. +
 connection-factory-name
 
-|
+| 
 | The JNDI name for a connection factory for transactional connections.
 
 For RBDMS, it must be a JNDI name that points to a javax.sql.DataSource object.
@@ -97,7 +97,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus. +
 connection-factory2-name
 
-|
+| 
 | The JNDI name for a connection factory for non-transactional connections.
 
 For RBDMS, it must be a JNDI name that points to a javax.sql.DataSource object.
@@ -113,7 +113,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus. +
 connection-password-decrypter
 
-|
+| 
 | Name of a class that implements ``DecryptionProvider`` and should only be specified if the password is encrypted in the persistence properties.
 
 See also ``json`` (camelCasing instead of kebab-casing).
@@ -126,7 +126,7 @@ NOTE: this config property isn't used by the framework, but is provided as a con
 isis.persistence.jdo-datanucleus. +
 impl.datanucleus.identifier.case
 
-|
+| 
 | null
 
 
@@ -150,7 +150,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus.objectProvider. +
 className
 
-|
+| 
 | null
 
 
@@ -176,7 +176,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus. +
 persistenceByReachabilityAtCommit
 
-|
+| 
 | null
 
 
@@ -186,7 +186,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus.schema. +
 auto-create-all
 
-|
+| 
 | Whether DN should automatically create the database schema on bootstrapping.
 
 This should be set to ``true`` when running against an in-memory database, but set to ``false`` when running against a persistent database (use something like flyway instead to manage schema evolution).
@@ -202,7 +202,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus.schema. +
 auto-create-database
 
-|
+| 
 | Previously we defaulted this property to "true", but that could cause the target database to be modified
 
 See also ``json`` (camelCasing instead of kebab-casing).
@@ -216,7 +216,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus.schema. +
 autoCreateAll
 
-|
+| 
 | null
 
 
@@ -226,7 +226,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus.schema. +
 autoCreateDatabase
 
-|
+| 
 | null
 
 
@@ -248,7 +248,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus.schema. +
 validateAll
 
-|
+| 
 | null
 
 
@@ -258,7 +258,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus.schema. +
 validateConstraints
 
-|
+| 
 | null
 
 
@@ -268,7 +268,7 @@ isis.persistence.jdo-datanucleus. +
 impl.datanucleus.schema. +
 validateTables
 
-|
+| 
 | null
 
 
@@ -277,7 +277,7 @@ validateTables
 isis.persistence.jdo-datanucleus. +
 impl.datanucleus.transaction-type
 
-|
+| 
 | Type of transaction to use.
 
 If running under JavaSE the default is RESOURCE_LOCAL, and if running under JavaEE the default is JTA.
@@ -293,7 +293,7 @@ isis.persistence.jdo-datanucleus. +
 impl.javax.jdo. +
 PersistenceManagerFactoryClass
 
-|
+| 
 | null
 
 
@@ -303,7 +303,7 @@ isis.persistence.jdo-datanucleus. +
 impl.javax.jdo.option. +
 ConnectionDriverName
 
-|
+| 
 | null
 
 
@@ -313,7 +313,7 @@ isis.persistence.jdo-datanucleus. +
 impl.javax.jdo.option. +
 ConnectionPassword
 
-|
+| 
 | null
 
 
@@ -323,7 +323,7 @@ isis.persistence.jdo-datanucleus. +
 impl.javax.jdo.option. +
 ConnectionURL
 
-|
+| 
 | null
 
 
@@ -333,7 +333,7 @@ isis.persistence.jdo-datanucleus. +
 impl.javax.jdo.option. +
 ConnectionUserName
 
-|
+| 
 | null
 
 
@@ -343,7 +343,7 @@ isis.persistence.jdo-datanucleus. +
 impl.javax.jdo.option. +
 connection-driver-name
 
-|
+| 
 | JDBC driver used by JDO/DataNucleus object store to connect.
 
 See also ``json`` (PascalCasing instead of kebab-casing).
@@ -357,7 +357,7 @@ isis.persistence.jdo-datanucleus. +
 impl.javax.jdo.option. +
 connection-password
 
-|
+| 
 | Password for the user account used by JDO/DataNucleus object store to connect.
 
 See also ``json`` (PascalCasing instead of kebab-casing).
@@ -371,7 +371,7 @@ isis.persistence.jdo-datanucleus. +
 impl.javax.jdo.option. +
 connection-url
 
-|
+| 
 | URL used by JDO/DataNucleus object store to connect.
 
 See also ``json`` (PascalCasing instead of kebab-casing).
@@ -385,7 +385,7 @@ isis.persistence.jdo-datanucleus. +
 impl.javax.jdo.option. +
 connection-user-name
 
-|
+| 
 | User account used by JDO/DataNucleus object store to connect.
 
 See also ``json`` (PascalCasing instead of kebab-casing).
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.security.shiro.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.security.shiro.adoc
index f18f5a9..dac0e41 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.security.shiro.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.security.shiro.adoc
@@ -17,7 +17,7 @@ isis.security.shiro. +
 auto-logout-if-already- +
 authenticated
 
-|
+| 
 | If the Shiro subject is found to be still authenticated, then will be logged out anyway and then re-authenticated.
 
 Applies only to the Restful Objects viewer.
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.testing.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.testing.adoc
index d0091dd..c910945 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.testing.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.testing.adoc
@@ -16,7 +16,7 @@ include::../section-hooks/isis.testing~pre.adoc[]
 isis.testing.fixtures. +
 initial-script
 
-|
+| 
 | Indicates the fixture script class to run initially.
 
 Intended for use when prototyping against an in-memory database (but will run in production mode as well if required).
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.value-types.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.value-types.adoc
index 3aaa4ff..a026acc 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.value-types.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.value-types.adoc
@@ -16,7 +16,7 @@ include::../section-hooks/isis.value-types~pre.adoc[]
 isis.value-types.primitives. +
 integer.format
 
-|
+| 
 | null
 
 
@@ -25,7 +25,7 @@ integer.format
 isis.value-types.java-lang.byte. +
 format
 
-|
+| 
 | Configures the number format understood by ``ByteValueSemanticsProviderAbstract``. @deprecated
 
 
@@ -34,7 +34,7 @@ format
 isis.value-types.java-lang.double. +
 format
 
-|
+| 
 | Configures the number format understood by ``DoubleValueSemanticsProviderAbstract``. @deprecated
 
 
@@ -43,7 +43,7 @@ format
 isis.value-types.java-lang.float. +
 format
 
-|
+| 
 | Configures the number format understood by ``FloatValueSemanticsProviderAbstract``. @deprecated
 
 
@@ -52,7 +52,7 @@ format
 isis.value-types.java-lang.long. +
 format
 
-|
+| 
 | Configures the number format understood by ``LongValueSemanticsProviderAbstract``. @deprecated
 
 
@@ -61,7 +61,7 @@ format
 isis.value-types.java-lang.short. +
 format
 
-|
+| 
 | Configures the number format understood by ``ShortValueSemanticsProviderAbstract``. @deprecated
 
 
@@ -70,7 +70,7 @@ format
 isis.value-types.java-math. +
 big-decimal.format
 
-|
+| 
 | Configures the number format understood by ``BigDecimalValueSemanticsProvider``. @deprecated
 
 
@@ -79,7 +79,7 @@ big-decimal.format
 isis.value-types.java-math. +
 big-integer.format
 
-|
+| 
 | Configures the number format understood by ``BigIntegerValueSemanticsProvider``. @deprecated
 
 
@@ -205,7 +205,7 @@ format
 isis.value-types.primitives.int. +
 format
 
-|
+| 
 | Configures the number format understood by ``IntValueSemanticsProviderAbstract``. @deprecated
 
 
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.viewer.restfulobjects.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.viewer.restfulobjects.adoc
index 3fcf60d..b0933aa 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.viewer.restfulobjects.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.viewer.restfulobjects.adoc
@@ -16,7 +16,7 @@ include::../section-hooks/isis.viewer.restfulobjects~pre.adoc[]
 isis.viewer.restfulobjects. +
 base-uri
 
-|
+| 
 | If left unset (the default), then the RO viewer will use the ``UriInfo`` (injected using ``Context``) to figure out the base Uri (used to render ``href``s).
 
 This will be correct much of the time, but will almost certainly be wrong if there is a reverse proxy.
@@ -29,7 +29,7 @@ If set, eg ``com/``, then this value will be used instead.
 isis.viewer.restfulobjects. +
 honor-ui-hints
 
-|
+| 
 | Whether to enable the ``x-ro-follow-links`` support, to minimize round trips.
 
 The RO viewer provides the capability for the client to set the optional ``x-ro-follow-links`` 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.
@@ -42,7 +42,7 @@ By default this functionality is disabled, this configuration property enables t
 isis.viewer.restfulobjects. +
 object-property-values-only
 
-|
+| 
 | 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.
 
 This is disabled by default. If enabled, then the representations returned are non-standard with respect to the RO Spec v1.0.
@@ -53,7 +53,7 @@ This is disabled by default. If enabled, then the representations returned are n
 isis.viewer.restfulobjects. +
 strict-accept-checking
 
-|
+| 
 | If set, then any unrecognised ``Accept`` headers will result in an HTTP _Not Acceptable_ response code (406).
 
 
@@ -62,7 +62,7 @@ strict-accept-checking
 isis.viewer.restfulobjects. +
 suppress-described-by-links
 
-|
+| 
 | If set, then the representations returned will omit any links to the formal domain-type representations.
 
 
@@ -71,7 +71,7 @@ suppress-described-by-links
 isis.viewer.restfulobjects. +
 suppress-member-disabled-reason
 
-|
+| 
 | If set, then - should there be an interaction with an action, property or collection that is disabled - then this will prevent the ``disabledReason`` reason from being added to the returned representation.
 
 This is disabled by default. If enabled, then the representations returned are non-standard with respect to the RO Spec v1.0.
@@ -82,7 +82,7 @@ This is disabled by default. If enabled, then the representations returned are n
 isis.viewer.restfulobjects. +
 suppress-member-extensions
 
-|
+| 
 | If set, then the ``x-isis-format`` key (under ``extensions``) for properties will be suppressed.
 
 This is disabled by default. If enabled, then the representations returned are non-standard with respect to the RO Spec v1.0.
@@ -93,7 +93,7 @@ This is disabled by default. If enabled, then the representations returned are n
 isis.viewer.restfulobjects. +
 suppress-member-id
 
-|
+| 
 | If set, then the ``id`` key for all members will be suppressed.
 
 This is disabled by default. If enabled, then the representations returned are non-standard with respect to the RO Spec v1.0.
@@ -104,7 +104,7 @@ This is disabled by default. If enabled, then the representations returned are n
 isis.viewer.restfulobjects. +
 suppress-member-links
 
-|
+| 
 | If set, then the detail link (in other words ``]``) for all members will be suppressed.
 
 This is disabled by default. If enabled, then the representations returned are non-standard with respect to the RO Spec v1.0.
@@ -115,7 +115,7 @@ This is disabled by default. If enabled, then the representations returned are n
 isis.viewer.restfulobjects. +
 suppress-update-link
 
-|
+| 
 | If set, then the update link (in other words `` ]`` to perform a bulk update of an object) will be suppressed.
 
 This is disabled by default. If enabled, then the representations returned are non-standard with respect to the RO Spec v1.0.
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.viewer.wicket.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.viewer.wicket.adoc
index 079205b..bd7ce2a 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.viewer.wicket.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.viewer.wicket.adoc
@@ -15,7 +15,7 @@ include::../section-hooks/isis.viewer.wicket~pre.adoc[]
 [[isis.viewer.wicket.ajax-debug-mode]]
 isis.viewer.wicket.ajax-debug-mode
 
-|
+| 
 | Whether the Ajax debug should be shown, by default this is disabled.
 
 
@@ -36,7 +36,7 @@ There is usually very little reason to change this from its default.
 isis.viewer.wicket.application. +
 about
 
-|
+| 
 | Label used on the about page.
 
 
@@ -45,7 +45,7 @@ about
 isis.viewer.wicket.application. +
 brand-logo-header
 
-|
+| 
 | Either the location of the image file (relative to the class-path resource root), or an absolute URL.
 
 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.
@@ -56,7 +56,7 @@ This is rendered on the header panel. An image with a size of 160x40 works well.
 isis.viewer.wicket.application. +
 brand-logo-signin
 
-|
+| 
 | Either the location of the image file (relative to the class-path resource root), or an absolute URL.
 
 This is rendered on the sign-in page. An image with a size of 400x40 works well. If not specified, the application name is used instead.
@@ -66,7 +66,7 @@ This is rendered on the sign-in page. An image with a size of 400x40 works well.
 [[isis.viewer.wicket.application.css]]
 isis.viewer.wicket.application.css
 
-|
+| 
 | URL of file to read any custom CSS, relative to ``static`` package on the class path.
 
 A typical value is ``css``. This will result in this file being read from the ``static/css`` directory (because static resources such as CSS are mounted by Spring by default under ``static`` package).
... 15760 lines suppressed ...