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/09 07:31:51 UTC

[isis] 01/03: ISIS-2222: reworking command, lots of stuff here...

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 bfffbd4fa4f72e734fdb399cfe4b9f479d4c959f
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Sep 4 10:58:07 2020 +0100

     ISIS-2222: reworking command, lots of stuff here...
---
 .../applib-ant/examples/annotation/Action.java     |   1 -
 .../applib-ant/examples/annotation/Property.java   |   1 -
 .../modules/applib-ant/pages/Action/command.adoc   |   1 +
 .../mixins/layout/Object_rebuildMetamodel.java     |   1 -
 .../examples/services/command/Command.java         |   3 -
 .../examples/services/command/CommandDefault.java  |   2 -
 .../ContentMappingServiceForCommandDto.java        |   1 -
 .../org/apache/isis/applib/annotation/Action.java  |  32 -
 .../isis/applib/annotation/CommandExecuteIn.java   |  83 ---
 .../isis/applib/annotation/CommandPersistence.java |  52 --
 .../isis/applib/annotation/CommandReification.java |  56 --
 .../apache/isis/applib/annotation/Property.java    |  34 +-
 .../mixins/layout/Object_rebuildMetamodel.java     |  22 +-
 .../isis/applib/services/DomainChangeAbstract.java | 296 ---------
 .../isis/applib/services/DomainChangeRecord.java   | 155 +++++
 .../DomainChangeRecord_openTargetObject.java       |  61 ++
 .../background/BackgroundCommandService.java       |  59 --
 .../isis/applib/services/bookmark/Bookmark.java    |   1 +
 .../isis/applib/services/command/Command.java      | 412 +++++--------
 .../applib/services/command/CommandContext.java    |   7 -
 .../applib/services/command/CommandDefault.java    | 186 ------
 .../CommandDtoProcessorForActionAbstract.java      |   4 +-
 .../CommandDtoProcessorForPropertyAbstract.java    |   4 +-
 .../services/command/CommandExecutorService.java   |   4 +-
 .../services/command/spi/CommandService.java       |   2 +-
 .../ContentMappingServiceForCommandDto.java        |  30 +-
 .../java/org/apache/isis/applib/util/ToString.java |  17 +-
 .../isis/applib/util/schema/CommandDtoUtils.java   |   4 +
 .../apache/isis/core/config/IsisConfiguration.java |   5 +-
 .../command/CommandFacetForActionAnnotation.java   |   5 +-
 ...ommandFacetForActionAnnotationAsConfigured.java |   2 -
 .../command/CommandFacetFromConfiguration.java     |   4 +-
 .../actions/action/invocation/CommandUtil.java     |  26 +
 .../facets/actions/command/CommandFacet.java       |  16 -
 .../actions/command/CommandFacetAbstract.java      |  38 --
 .../command/CommandFacetForPropertyAnnotation.java |   5 +-
 ...mandFacetForPropertyAnnotationAsConfigured.java |   2 -
 .../services/appfeat/ApplicationFeatureId.java     |   4 +-
 .../command/CommandDtoServiceInternal.java         |  17 +-
 .../specloader/specimpl/ObjectActionDefault.java   |  51 +-
 .../specloader/specimpl/ObjectMemberAbstract.java  |  55 +-
 .../specimpl/OneToOneAssociationDefault.java       |  36 +-
 .../valuetypes/ValueTypeProviderForBuiltin.java    |   7 +-
 .../ActionAnnotationFacetFactoryTest_Command.java  |   3 -
 .../background/BackgroundCommandExecution.java     |   1 -
 .../background/CommandExecutionAbstract.java       |   1 -
 .../command/CommandDtoServiceInternalDefault.java  |  22 +-
 .../command/CommandExecutorServiceDefault.java     |  42 +-
 .../command/CommandServiceDefault.java             |   3 +-
 examples/demo/domain/pom.xml                       |   8 +-
 .../src/main/java/demoapp/dom/DemoModule.java      |   5 +-
 .../ActionCommandDisabledMetaAnnotation.java       |   3 -
 .../Action/command/ActionCommandJdo.java           |   6 -
 .../Action/command/ActionCommandJdo.layout.xml     |   3 +-
 .../ActionCommandJdo_backgroundCommands.java       |  39 --
 .../ActionCommandJdo_clearBackgroundCommands.java  |  38 --
 .../ActionCommandJdo_clearForegroundCommands.java  |  38 --
 ...ommands.java => ActionCommandJdo_commands.java} |  14 +-
 ...ActionCommandJdo_executeBackgroundCommands.java |  39 --
 .../spiimpl/CommandServiceSpiForActions.java       |  95 ---
 .../impl/IsisModuleExtCommandLogImpl.java          |  10 +-
 .../commandlog/impl/api/UserDataKeys.java          |  21 +-
 ...ndExecutionFromBackgroundCommandServiceJdo.java |  29 -
 .../background/BackgroundCommandServiceJdo.java    |  92 ---
 .../BackgroundCommandServiceJdoRepository.java     |  48 --
 .../extensions/commandlog/impl/jdo/CommandJdo.java | 672 +++++++--------------
 ...doRepository.java => CommandJdoRepository.java} | 121 ++--
 .../impl/jdo/CommandJdo_childCommands.java         |  23 +-
 .../impl/jdo/CommandJdo_openResultObject.java      |  18 +
 .../commandlog/impl/jdo/CommandJdo_retry.java      |  87 +--
 .../impl/jdo/CommandJdo_siblingCommands.java       |  31 +-
 .../commandlog/impl/jdo/CommandServiceJdo.java     |  99 ---
 .../commandlog/impl/jdo/ReplayState.java           |  34 ++
 .../impl/mixins/HasUniqueId_command.java           |   7 +-
 .../mixins/HasUsername_recentCommandsByUser.java   |   5 +-
 .../impl/mixins/Object_recentCommands.java         |   4 +-
 .../impl/mixins/T_backgroundCommands.java          |   7 +-
 .../commandlog/impl/ui/CommandServiceMenu.java     |   7 +-
 .../CommandReplayAnalyserExceptionStr.java         |   1 -
 ...mandReplayAnalyserNumberBackgroundCommands.java |   9 +-
 .../analysis/CommandReplayAnalyserResultStr.java   |   1 -
 .../analysis/CommandReplayAnalysisService.java     |   3 +-
 .../executor/CommandExecutorServiceWithTime.java   |   9 +-
 .../impl/executor/ReplayableCommandExecution.java  |  19 +-
 .../impl/mixins/CommandJdo_download.java           |   3 +-
 .../impl/mixins/CommandJdo_exclude.java            |   1 -
 .../impl/mixins/CommandJdo_replayNext.java         |  13 +-
 .../impl/mixins/CommandJdo_replayQueue.java        |   6 +-
 ...kgroundCommandsWithReplicationAndReplayJob.java |   1 -
 .../impl/ui/CommandReplayOnMasterService.java      |   7 +-
 .../impl/ui/CommandReplayOnSlaveService.java       |   9 +-
 .../quartz/jobs/RunBackgroundCommandsJob.java      |   4 -
 .../IsisBookmarkConverter.java}                    |  16 +-
 .../IsisLocalResourcePathConverter.java            |   2 +-
 .../{ => applib}/IsisMarkupConverter.java          |   2 +-
 .../{ => applib}/IsisPasswordConverter.java        |   2 +-
 .../v2/IsisChangesDtoConverter.java}               |  15 +-
 .../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/PersistenceSession5.java           |   3 +-
 .../datanucleus-5/src/main/resources/plugin.xml    |  96 ++-
 .../components/scalars/bookmark/BookmarkPanel.java |  29 +-
 .../scalars/bookmark/BookmarkPanelFactory.java     |  27 +-
 .../ui/components/scalars/oiddto/OidDtoPanel.java  |  27 +-
 .../scalars/oiddto/OidDtoPanelFactory.java         |  25 +-
 .../wicket/ui/panels/FormExecutorDefault.java      |  12 +-
 .../ComponentFactoryRegistrarDefault.java          |   7 +
 111 files changed, 1184 insertions(+), 2702 deletions(-)

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..26d5ca3 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
@@ -27,7 +27,6 @@ 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;
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..cc2d0a9 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
@@ -27,7 +27,6 @@ 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;
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..075c67a 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.
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..82b7cc4 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;
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..afa0be6 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
@@ -22,11 +22,8 @@ import java.sql.Timestamp;
 
 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.services.HasUniqueId;
-import org.apache.isis.applib.services.background.BackgroundCommandService;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.applib.services.iactn.Interaction;
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
index 069acd4..e24b08b 100644
--- 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
@@ -21,8 +21,6 @@ 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;
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
index a4cea23..ef6925f 100644
--- 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
@@ -34,7 +34,6 @@ 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;
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..8d4a22f 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
@@ -27,7 +27,6 @@ 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;
@@ -91,37 +90,6 @@ public @interface Action {
 
     // end::refguide[]
     /**
-     * Whether the action invocation should be reified into a {@link org.apache.isis.applib.services.command.Command} object.
-     */
-    // tag::refguide[]
-    CommandReification command()                                    // <.>
-            default CommandReification.NOT_SPECIFIED;
-
-    // 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/CommandReification.java b/api/applib/src/main/java/org/apache/isis/applib/annotation/CommandReification.java
deleted file mode 100644
index a2b8b68..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/annotation/CommandReification.java
+++ /dev/null
@@ -1,56 +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;
-
-/**
- * The available policies as to whether action invocations are reified into commands.
- */
-// tag::refguide[]
-public enum CommandReification {
-    // end::refguide[]
-    /**
-     * Whether the action should be handled as a command as per the default command configured in <tt>applicationp.properties</tt>.
-     *
-     * <p>
-     *     If no command policy is configured, then the action is <i>not</i> treated as a command.
-     * </p>
-     */
-    // tag::refguide[]
-    AS_CONFIGURED,
-    // end::refguide[]
-    /**
-     * Handle the action as a command, irrespective of any configuration settings.
-     */
-    // tag::refguide[]
-    ENABLED,
-    // end::refguide[]
-    /**
-     * Do not handle the action as a command, irrespective of any configuration settings.
-     */
-    // tag::refguide[]
-    DISABLED,
-    // end::refguide[]
-    /**
-     * Ignore the value provided by this annotation (meaning that the framework will keep searching, in meta
-     * annotations or superclasses/interfaces).
-     */
-    // tag::refguide[]
-    NOT_SPECIFIED
-}
-// 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..1488a33 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
@@ -27,7 +27,6 @@ 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;
@@ -52,37 +51,6 @@ public @interface Property {
 
     // end::refguide[]
     /**
-     * Whether the property edit should be reified into a {@link org.apache.isis.applib.services.command.Command} object.
-     */
-    // tag::refguide[]
-    CommandReification command()                                // <.>
-            default CommandReification.NOT_SPECIFIED;
-
-    // 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>
@@ -210,7 +178,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/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/DomainChangeAbstract.java b/api/applib/src/main/java/org/apache/isis/applib/services/DomainChangeAbstract.java
deleted file mode 100644
index 4a19bd6..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/services/DomainChangeAbstract.java
+++ /dev/null
@@ -1,296 +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;
-
-import java.sql.Timestamp;
-import java.util.UUID;
-
-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.extern.log4j.Log4j2;
-
-
-/**
- * An abstraction of some sort of recorded change to a domain object: commands, audit entries or published events.
- */
-@Log4j2
-public abstract class DomainChangeAbstract
-        implements HasUniqueId, HasUsername {
-
-    public static enum ChangeType {
-        COMMAND,
-        AUDIT_ENTRY,
-        PUBLISHED_INTERACTION;
-        @Override
-        public String toString() {
-            return name().replace("_", " ");
-        }
-    }
-    public DomainChangeAbstract(final ChangeType changeType) {
-        this.type = changeType;
-    }
-
-    /**
-     * 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")
-    @Getter
-    private final ChangeType type;
-
-
-
-    /**
-     * The user that caused the change.
-     *
-     * <p>
-     * This dummy implementation is a trick so that Isis will render the property in a standalone table.  Each of the
-     * subclasses override with the &quot;real&quot; implementation.
-     */
-    @Property
-    @MemberOrder(name="Identifiers", sequence = "10")
-    public String getUser() {
-        return null;
-    }
-
-
-
-    /**
-     * The time that the change occurred.
-     *
-     * <p>
-     * This dummy implementation is a trick so that Isis will render the property in a standalone table.  Each of the
-     * subclasses override with the &quot;real&quot; implementation.
-     */
-    @Property
-    @MemberOrder(name="Identifiers", sequence = "20")
-    public Timestamp getTimestamp() {
-        return null;
-    }
-
-
-    /**
-     * The unique identifier (a GUID) of the transaction in which this change occurred.
-     *
-     * <p>
-     * This dummy implementation is a trick so that Isis will render the property in a standalone table.  Each of the
-     * subclasses override with the &quot;real&quot; implementation.
-     */
-    @Property
-    @MemberOrder(name="Identifiers",sequence = "50")
-    public UUID getTransactionId() {
-        return null;
-    }
-
-
-    /**
-     * The class of the domain object being changed.
-     *
-     * <p>
-     * This dummy implementation is a trick so that Isis will render the property in a standalone table.  Each of the
-     * subclasses override with the &quot;real&quot; implementation.
-     */
-    @Property
-    @PropertyLayout(named="Class")
-    @MemberOrder(name="Target", sequence = "10")
-    public String getTargetClass() {
-        return null;
-    }
-
-
-
-    @Programmatic
-    public Bookmark getTarget() {
-        final String str = getTargetStr();
-        return Bookmark.parse(str).orElse(null);
-    }
-
-    @Programmatic
-    public void setTarget(Bookmark target) {
-        final String targetStr = target != null ? target.toString() : null;
-        setTargetStr(targetStr);
-    }
-
-
-    /**
-     * 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>
-     *
-     * <p>
-     * This dummy implementation is a trick so that Isis will render the property in a standalone table.  Each of the
-     * subclasses override with the &quot;real&quot; implementation.
-     * </p>
-     *
-     * <p>
-     *     NB: commands and published events applied only to actions, hence the name of this field.  In a future release
-     *     the name of this field may change to &quot;TargetMember&quot;.  Note that the {@link PropertyLayout} already uses
-     *     &quot;Member&quot; this as a name hint.
-     * </p>
-     *
-     */
-    @Property(optionality = Optionality.OPTIONAL)
-    @PropertyLayout(
-            named="Member",
-            hidden = Where.ALL_EXCEPT_STANDALONE_TABLES
-    )
-    @MemberOrder(name="Target", sequence = "20")
-    @Getter
-    private String targetAction;
-
-
-
-    /**
-     * The (string representation of the) {@link Bookmark} identifying the domain object that has changed.
-     *
-     * <p>
-     * This dummy implementation is a trick so that Isis will render the property in a standalone table.  Each of the
-     * subclasses override with the &quot;real&quot; implementation.
-     */
-    @Property
-    @PropertyLayout(named="Object")
-    @MemberOrder(name="Target", sequence="30")
-    public String getTargetStr() {
-        return null;
-    }
-
-    /**
-     * For {@link #setTarget(Bookmark)} to delegate to.
-     */
-    public abstract void setTargetStr(final String targetStr);
-
-
-
-    @Action(semantics = SemanticsOf.SAFE)
-    @ActionLayout(named = "Open")
-    @MemberOrder(name="TargetStr", sequence="1")
-    public Object openTargetObject() {
-        try {
-            return bookmarkService != null
-                    ? bookmarkService.lookup(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 getTarget() == null;
-    }
-
-    public String disableOpenTargetObject() {
-        final Object targetObject = getTarget();
-        if (targetObject == null) {
-            return null;
-        }
-        final BeanSort sortOfObject = metaModelService.sortOf(getTarget(), MetaModelService.Mode.RELAXED);
-        return !(sortOfObject.isViewModel() || sortOfObject.isEntity())
-                ? "Can only open view models or entities"
-                : null;
-    }
-
-
-
-    /**
-     * The property of the object that was changed.
-     *
-     * <p>
-     * Populated only for audit entries.
-     *
-     * <p>
-     * This dummy implementation is a trick so that Isis will render the property in a standalone table.  Each of the
-     * subclasses override with the &quot;real&quot; implementation.
-     */
-    @Property(optionality = Optionality.OPTIONAL)
-    @PropertyLayout(hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
-    @MemberOrder(name="Target",sequence = "21")
-    public String getPropertyId() {
-        return null;
-    }
-
-
-    /**
-     * The value of the property prior to it being changed.
-     *
-     * <p>
-     * Populated only for audit entries.
-     *
-     * <p>
-     * This dummy implementation is a trick so that Isis will render the property in a standalone table.  Each of the
-     * subclasses override with the &quot;real&quot; implementation.
-     */
-    @Property(optionality = Optionality.OPTIONAL)
-    @PropertyLayout(hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
-    @MemberOrder(name="Detail",sequence = "6")
-    public String getPreValue() {
-        return null;
-    }
-
-
-    /**
-     * The value of the property after it has changed.
-     *
-     * <p>
-     * Populated only for audit entries.
-     *
-     * <p>
-     * This dummy implementation is a trick so that Isis will render the property in a standalone table.  Each of the
-     * subclasses override with the &quot;real&quot; implementation.
-     */
-    @Property(optionality = Optionality.MANDATORY)
-    @PropertyLayout(hidden = Where.ALL_EXCEPT_STANDALONE_TABLES)
-    @MemberOrder(name="Detail",sequence = "7")
-    public String getPostValue() {
-        return null;
-    }
-
-
-    @javax.inject.Inject
-    protected BookmarkService bookmarkService;
-
-    @javax.inject.Inject
-    protected MessageService messageService;
-
-    @javax.inject.Inject
-    protected MetaModelService metaModelService;
-
-}
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..ea81d3f
--- /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="Class")
+    @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/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/command/Command.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/Command.java
index aa30c57..5967960 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,201 +19,131 @@
 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.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.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 static org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling.toTimestamp;
+
+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 {
 
-    // end::refguide[]
-    /**
-     * The user that created the command.
-     */
-    // tag::refguide[]
-    String getUser();                           // <.>
-    // end::refguide[]
+    public Command() {
+        this(UUID.randomUUID());
+    }
+    public Command(final Command parent, final CommandDto commandDto) {
+        this(UUID.fromString(commandDto.getTransactionId()));
+        internal().setCommandDto(commandDto);
+        internal().setParent(parent);
+        internal().setUsername(commandDto.getUser());
+        internal().setTimestamp(toTimestamp(commandDto.getTimestamp()));
+    }
 
-    /**
-     * The date/time at which this command was created.
-     */
-    // tag::refguide[]
-    Timestamp getTimestamp();                   // <.>
-    // end::refguide[]
+    private Command(final UUID uuid) {
+        this.uniqueId = uuid;
+    }
 
-    /**
-     * {@link Bookmark} of the target object (entity or service) on which this action was performed.
-     *
-     * <p>
-     * Will only be populated if a {@link BookmarkService} has been configured.
-     * </p>
-     */
-    // 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[]
+    @Getter
+    private UUID uniqueId;
 
+    // end::refguide[]
     /**
-     * A human-friendly description of the class of the target object.
+     * The user that created the command.
      */
     // tag::refguide[]
-    String getTargetClass();                    // <.>
+    @Getter
+    private String username;                    // <.>
     // end::refguide[]
 
     /**
-     * The human-friendly name of the action invoked/property edited on the target object.
+     * The date/time at which this command was created.
      */
     // tag::refguide[]
-    String getTargetAction();                   // <.>
+    @Getter
+    private Timestamp timestamp;                // <.>
     // end::refguide[]
 
     /**
-     * A human-friendly description of the arguments with which the action was invoked.
+     * Serializable representation of the action invocation/property edit.
      */
     // tag::refguide[]
-    String getArguments();                      // <.>
+    @Getter
+    private CommandDto commandDto;              // <.>
     // end::refguide[]
 
     /**
-     *
-     * A formal (XML or similar) specification of the action to invoke/being invoked.
+     * Also available in {@link #getCommandDto()}, is the {@link Bookmark} of
+     * the target object (entity or service) on which this action/edit was performed.
      */
     // tag::refguide[]
-    String getMemento();                        // <.>
+    @Getter
+    private Bookmark target;                    // <.>
     // 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}.
+     * Also available in {@link #getCommandDto()}, holds a string
+     * representation of the invoked action, or the edited property.
      */
     // tag::refguide[]
-    CommandExecuteIn getExecuteIn();            // <.>
+    @Getter
+    private String logicalMemberIdentifier;     // <.>
     // 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
-    }
-    // end::refguide2[]
-
     /**
-     * The (current) executor of this command.
+     * For commands created through the {@link WrapperFactory} (using
+     * {@link WrapperFactory#asyncWrap(Object, AsyncControl)} or
+     * {@link WrapperFactory#asyncWrapMixin(Class, Object, AsyncControl)}),
+     * 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.
-     *
-     * <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>
-     *
+     *     Will return <code>null</code> if there is no parent.
+     * </p>
      */
     // tag::refguide[]
-    Executor getExecutor();                     // <.>
+    @Getter
+    private Command parent;                     // <.>
     // end::refguide[]
 
     /**
@@ -230,7 +160,8 @@ public interface Command extends HasUniqueId {
      * {@link Interaction.Execution#getStartedAt()}.
      */
     // tag::refguide[]
-    Timestamp getStartedAt();                   // <.>
+    @Getter
+    private Timestamp startedAt;                // <.>
     // end::refguide[]
 
     /**
@@ -247,229 +178,170 @@ 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 WrapperFactory} 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.
-     *
-     * <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.
+     * Whether this command resulted in a change of state to the system.
      *
      * <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 #isPersistHint() 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 Command.Internal INTERNAL = new Internal();
 
+    public class Internal {
         /**
          * <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);
-
+        public void setUsername(String username) {
+            Command.this.username = username;
+        }
         /**
          * <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);
-
+        public void setTimestamp(Timestamp timestamp) {
+            Command.this.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).
+         *     Only populated for async commands created through the
+         *     {@link WrapperFactory}.
+         * </p>
          */
-        void setTarget(Bookmark target);
-
+        public void setParent(Command parent) {
+            Command.this.parent = parent;
+        }
         /**
          * <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>).
+         * Implementation notes: set when the action is invoked (in the <tt>ActionInvocationFacet</tt>).
+         * @param commandDto
          */
-        void setMemberIdentifier(String memberIdentifier);
-
+        public void setCommandDto(CommandDto commandDto) {
+            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>) or property edited
-         * (in the <tt>PropertySetterOrClearFacet</tt>).
+         * Implementation notes: set when the action is invoked (in the ActionInvocationFacet).
          */
-        void setTargetAction(String targetAction);
-
-
+        public void setTarget(Bookmark target) {
+            Command.this.target = target;
+        }
         /**
          * <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>).
+         * Implementation notes: set when the action is invoked (in
+         * <tt>ActionInvocationFacet</tt>) or property is edited (in
+         * <tt>PropertySetterFacet</tt>).
          */
-        void setArguments(final String arguments);
-
-        /**
-         * <b>NOT API</b>: intended to be called only by the framework.
-         */
-        void setExecutor(final Executor executor);
-
+        public void setLogicalMemberIdentifier(String logicalMemberIdentifier) {
+            Command.this.logicalMemberIdentifier = logicalMemberIdentifier;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setExecuteIn(CommandExecuteIn executeIn);
-
+        public void setStartedAt(Timestamp startedAt) {
+            Command.this.startedAt = startedAt;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setResult(Bookmark resultBookmark);
-
+        public void setCompletedAt(final Timestamp completed) {
+            Command.this.completedAt = completed;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setException(String stackTrace);
-
+        public void setResult(final Bookmark result) {
+            Command.this.result = result;
+        }
         /**
          * <b>NOT API</b>: intended to be called only by the framework.
          */
-        void setParent(final Command parent);
-
+        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 Interaction.Execution#setCompletedAt(Timestamp)}.
          */
-        void setCompletedAt(Timestamp completedAt);
-
-        /**
-         * <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 #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 Command.Internal internal() {
+        return INTERNAL;
+    }
+
 
 // tag::refguide[]
+
 }
 // end::refguide[]
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/CommandContext.java
index 4d07f9d..8e50213 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/CommandContext.java
@@ -30,7 +30,6 @@ 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 lombok.Getter;
@@ -69,12 +68,6 @@ public class CommandContext {
             serviceInjector.injectServicesInto(command);
         }
     }
-
-    public Optional<Executor> getCurrentExecutor() {
-        return Optional.ofNullable(getCommand())
-                .map(Command::getExecutor);
-    }
-
     // 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 da4e3e4..0000000
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDefault.java
+++ /dev/null
@@ -1,186 +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 void setExecuteIn(CommandExecuteIn executeIn) {
-            CommandDefault.this.executeIn = executeIn;
-        }
-    };
-
-    @Override
-    public Command.Internal internal() {
-        return INTERNAL;
-    }
-
-}
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessorForActionAbstract.java b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessorForActionAbstract.java
index 531c945..97c9780 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessorForActionAbstract.java
+++ b/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandDtoProcessorForActionAbstract.java
@@ -27,8 +27,8 @@ 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 CommandDto asDto(final Command command) {
+        return command.getCommandDto();
     }
     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/command/CommandDtoProcessorForPropertyAbstract.java
index 6bc4d69..b8e2dd3 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/command/CommandDtoProcessorForPropertyAbstract.java
@@ -26,8 +26,8 @@ import org.apache.isis.schema.cmd.v2.PropertyDto;
  */
 public abstract class CommandDtoProcessorForPropertyAbstract
 implements CommandDtoProcessor {
-    protected CommandDto asDto(final CommandWithDto commandWithDto) {
-        return commandWithDto.asDto();
+    protected CommandDto asDto(final Command commandWithDto) {
+        return commandWithDto.getCommandDto();
     }
     protected PropertyDto getPropertyDto(final CommandDto commandDto) {
         return (PropertyDto) commandDto.getMember();
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..c97685f 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,13 +48,13 @@ 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   // <.>
+            Command command                 // <.>
     );
 
     Bookmark executeCommand(
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
index eb3abce..ef1cf9c 100644
--- 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
@@ -49,7 +49,7 @@ public interface CommandService {
 
     /**
      * &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}
+     * persist it if its {@link Command#getPersistence()} and {@link Command#isSystemStateChanged() persistence hint}
      * indicate that it should be.
      *
      * <p>
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
index 480cb7b..b96e6d3 100644
--- 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
@@ -35,7 +35,6 @@ 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;
@@ -63,27 +62,27 @@ public class ContentMappingServiceForCommandDto implements ContentMappingService
     /**
      * Not part of the {@link ContentMappingService} API.
      */
-    public CommandDto map(final CommandWithDto commandWithDto) {
-        return asProcessedDto(commandWithDto);
+    public CommandDto map(final Command command) {
+        return asProcessedDto(command);
     }
 
     CommandDto asProcessedDto(final Object object) {
-        if (!(object instanceof CommandWithDto)) {
+        if (!(object instanceof Command)) {
             return null;
         }
-        final CommandWithDto commandWithDto = (CommandWithDto) object;
-        return asProcessedDto(commandWithDto);
+        final Command command = (Command) object;
+        return asProcessedDto(command);
     }
 
-    private CommandDto asProcessedDto(final CommandWithDto commandWithDto) {
-        if(commandWithDto == null) {
+    private CommandDto asProcessedDto(final Command command) {
+        if(command == null) {
             return null;
         }
-        CommandDto commandDto = commandWithDto.asDto();
+        CommandDto commandDto = command.getCommandDto();
 
         // global processors
         for (final CommandDtoProcessorService commandDtoProcessorService : commandDtoProcessorServices) {
-            commandDto = commandDtoProcessorService.process(commandWithDto, commandDto);
+            commandDto = commandDtoProcessorService.process(command, commandDto);
             if(commandDto == null) {
                 // any processor could return null, effectively breaking the chain.
                 return null;
@@ -96,7 +95,7 @@ public class ContentMappingServiceForCommandDto implements ContentMappingService
         if (commandDtoProcessor == null) {
             return commandDto;
         }
-        return commandDtoProcessor.process(commandWithDto, commandDto);
+        return commandDtoProcessor.process(command, commandDto);
     }
 
 
@@ -115,7 +114,7 @@ public class ContentMappingServiceForCommandDto implements ContentMappingService
         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());
+            commandDto.setUser(command.getUsername());
 
             // 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.
@@ -124,13 +123,6 @@ public class ContentMappingServiceForCommandDto implements ContentMappingService
                 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);
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 a11a431..8893742 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 bb7a815..9b6c93b0 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
@@ -63,6 +63,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));
     }
diff --git a/core/config/src/main/java/org/apache/isis/core/config/IsisConfiguration.java b/core/config/src/main/java/org/apache/isis/core/config/IsisConfiguration.java
index e17dacb..2b2ffa4 100644
--- a/core/config/src/main/java/org/apache/isis/core/config/IsisConfiguration.java
+++ b/core/config/src/main/java/org/apache/isis/core/config/IsisConfiguration.java
@@ -58,7 +58,6 @@ import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.LabelPosition;
 import org.apache.isis.applib.annotation.PromptStyle;
 import org.apache.isis.applib.services.audit.AuditerService;
-import org.apache.isis.applib.services.command.CommandWithDto;
 import org.apache.isis.applib.services.i18n.TranslationService;
 import org.apache.isis.applib.services.iactn.Interaction;
 import org.apache.isis.applib.services.publish.PublishedObjects;
@@ -656,7 +655,7 @@ public class IsisConfiguration {
                  * <p>
                  *     In particular, the {@link CommandWithDto} implementation
                  *     of {@link org.apache.isis.applib.services.command.Command} represents the action invocation
-                 *     memento (obtained using {@link CommandWithDto#asDto()}) as a
+                 *     memento (obtained using {@link CommandWithDto#getCommandDto()}) as a
                  *     {@link org.apache.isis.schema.cmd.v2.CommandDto}.
                  * </p>
                  *
@@ -843,7 +842,7 @@ public class IsisConfiguration {
                  * <p>
                  *     In particular, the {@link CommandWithDto} implementation
                  *     of {@link org.apache.isis.applib.services.command.Command} represents the action invocation
-                 *     memento (obtained using {@link CommandWithDto#asDto()}) as a
+                 *     memento (obtained using {@link CommandWithDto#getCommandDto()}) as a
                  *     {@link org.apache.isis.schema.cmd.v2.CommandDto}.
                  * </p>
                  *
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetForActionAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetForActionAnnotation.java
index 8b14832..8798f14 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetForActionAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetForActionAnnotation.java
@@ -21,9 +21,6 @@ package org.apache.isis.core.metamodel.facets.actions.action.command;
 import java.util.Optional;
 
 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.annotation.CommandReification;
 import org.apache.isis.applib.services.command.CommandDtoProcessor;
 import org.apache.isis.applib.services.inject.ServiceInjector;
 import org.apache.isis.core.config.IsisConfiguration;
@@ -116,7 +113,7 @@ public class CommandFacetForActionAnnotation extends CommandFacetAbstract {
             final CommandDtoProcessor processor,
             final FacetHolder holder,
             final ServiceInjector servicesInjector) {
-        super(persistence, executeIn, enablement, processor, holder, servicesInjector);
+        super(persistence, processor, holder, servicesInjector);
     }
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetForActionAnnotationAsConfigured.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetForActionAnnotationAsConfigured.java
index 9c89fd6..dca1c21 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetForActionAnnotationAsConfigured.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetForActionAnnotationAsConfigured.java
@@ -18,8 +18,6 @@
  */
 package org.apache.isis.core.metamodel.facets.actions.action.command;
 
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.services.inject.ServiceInjector;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetFromConfiguration.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetFromConfiguration.java
index 3103d57..0232d2c 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetFromConfiguration.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/command/CommandFacetFromConfiguration.java
@@ -19,8 +19,6 @@
 
 package org.apache.isis.core.metamodel.facets.actions.action.command;
 
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.services.inject.ServiceInjector;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facets.actions.command.CommandFacet;
@@ -40,7 +38,7 @@ public class CommandFacetFromConfiguration extends CommandFacetAbstract {
             final CommandExecuteIn executeIn,
             final FacetHolder holder,
             final ServiceInjector servicesInjector) {
-        super(persistence, executeIn, Enablement.ENABLED, null, holder, servicesInjector);
+        super(persistence, null, holder, servicesInjector);
     }
 
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/CommandUtil.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/CommandUtil.java
index 00509f0..f80fd79 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/CommandUtil.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/action/invocation/CommandUtil.java
@@ -32,6 +32,7 @@ import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
 import org.apache.isis.core.metamodel.spec.feature.ObjectMember;
+import org.apache.isis.core.metamodel.spec.feature.OneToOneAssociation;
 
 import lombok.val;
 
@@ -58,6 +59,31 @@ public class CommandUtil {
         return objectMember.getIdentifier().toClassAndNameIdentityString();
     }
 
+    public static String logicalMemberIdentifierFor(final ObjectMember objectMember) {
+        if(objectMember instanceof ObjectAction) {
+            return logicalMemberIdentifierFor((ObjectAction)objectMember);
+        }
+        if(objectMember instanceof OneToOneAssociation) {
+            return logicalMemberIdentifierFor((OneToOneAssociation)objectMember);
+        }
+        throw new IllegalArgumentException(objectMember.getClass() + " is not supported");
+    }
+
+    public static String logicalMemberIdentifierFor(final ObjectAction objectAction) {
+        return logicalMemberIdentifierFor(objectAction.getOnType(), objectAction);
+    }
+
+    public static String logicalMemberIdentifierFor(final OneToOneAssociation otoa) {
+        return logicalMemberIdentifierFor(otoa.getOnType(), otoa);
+    }
+
+    private static String logicalMemberIdentifierFor(
+            final ObjectSpecification onType, final ObjectMember objectMember) {
+        final String objectType = onType.getSpecId().asString();
+        final String localId = objectMember.getIdentifier().toNameIdentityString();
+        return objectType + "#" + localId;
+    }
+
     public static String argDescriptionFor(final ManagedObject valueAdapter) {
         final StringBuilder buf = new StringBuilder();
         if(valueAdapter != null) {
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/command/CommandFacet.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/command/CommandFacet.java
index cfc278b..314e6d9 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/command/CommandFacet.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/command/CommandFacet.java
@@ -19,9 +19,6 @@
 
 package org.apache.isis.core.metamodel.facets.actions.command;
 
-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.services.command.Command;
 import org.apache.isis.applib.services.command.CommandDtoProcessor;
 import org.apache.isis.core.metamodel.facetapi.Facet;
@@ -38,18 +35,5 @@ public interface CommandFacet extends Facet {
 
     public CommandPersistence persistence();
 
-    public CommandExecuteIn executeIn();
-
-    /**
-     * Indicates that the action to which this {@link Facet} is
-     * attached should <i>not</i> be treated as an action.
-     *
-     * <p>
-     * Exists to allow implementations that configure all actions to be treated as
-     * commands, but which can then be disabled for selected actions (eg using
-     * {@link Action#command()}).
-     */
-    public boolean isDisabled();
-
     public CommandDtoProcessor getProcessor();
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/command/CommandFacetAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/command/CommandFacetAbstract.java
index d51e342..8f53327 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/command/CommandFacetAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/actions/command/CommandFacetAbstract.java
@@ -21,8 +21,6 @@ package org.apache.isis.core.metamodel.facets.actions.command;
 
 import java.util.Map;
 
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.services.command.CommandDtoProcessor;
 import org.apache.isis.applib.services.inject.ServiceInjector;
 import org.apache.isis.core.metamodel.facetapi.Facet;
@@ -35,32 +33,14 @@ public abstract class CommandFacetAbstract extends FacetAbstract implements Comm
         return CommandFacet.class;
     }
 
-    public enum Enablement {
-        DISABLED,
-        ENABLED;
-
-        public static Enablement isDisabled(boolean disabled) {
-            return disabled ? DISABLED: ENABLED;
-        }
-    }
-
-    private final CommandPersistence persistence;
-    private final CommandExecuteIn executeIn;
-    private final Enablement enablement;
     private final CommandDtoProcessor processor;
 
     public CommandFacetAbstract(
-            final CommandPersistence persistence,
-            final CommandExecuteIn executeIn,
-            final Enablement enablement,
             final CommandDtoProcessor processor,
             final FacetHolder holder,
             final ServiceInjector servicesInjector) {
         super(type(), holder);
         inject(processor, servicesInjector);
-        this.persistence = persistence;
-        this.executeIn = executeIn;
-        this.enablement = enablement;
         this.processor = processor;
     }
 
@@ -73,21 +53,6 @@ public abstract class CommandFacetAbstract extends FacetAbstract implements Comm
     }
 
     @Override
-    public CommandPersistence persistence() {
-        return this.persistence;
-    }
-
-    @Override
-    public CommandExecuteIn executeIn() {
-        return executeIn;
-    }
-
-    @Override
-    public boolean isDisabled() {
-        return this.enablement == Enablement.DISABLED;
-    }
-
-    @Override
     public CommandDtoProcessor getProcessor() {
         return processor;
     }
@@ -115,9 +80,6 @@ public abstract class CommandFacetAbstract extends FacetAbstract implements Comm
 
     @Override public void appendAttributesTo(final Map<String, Object> attributeMap) {
         super.appendAttributesTo(attributeMap);
-        attributeMap.put("executeIn", executeIn);
-        attributeMap.put("persistence", persistence);
-        attributeMap.put("disabled", isDisabled());
         attributeMap.put("dtoProcessor", processor);
     }
 }
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/command/CommandFacetForPropertyAnnotation.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/command/CommandFacetForPropertyAnnotation.java
index 8a551cb..558ba6a 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/command/CommandFacetForPropertyAnnotation.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/command/CommandFacetForPropertyAnnotation.java
@@ -20,9 +20,6 @@ package org.apache.isis.core.metamodel.facets.properties.property.command;
 
 import java.util.Optional;
 
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
-import org.apache.isis.applib.annotation.CommandReification;
 import org.apache.isis.applib.annotation.Property;
 import org.apache.isis.applib.services.command.CommandDtoProcessor;
 import org.apache.isis.applib.services.inject.ServiceInjector;
@@ -92,7 +89,7 @@ public class CommandFacetForPropertyAnnotation extends CommandFacetAbstract {
             final FacetHolder holder,
             final CommandDtoProcessor processor,
             final ServiceInjector servicesInjector) {
-        super(persistence, executeIn, enablement, processor, holder, servicesInjector);
+        super(persistence, processor, holder, servicesInjector);
     }
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/command/CommandFacetForPropertyAnnotationAsConfigured.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/command/CommandFacetForPropertyAnnotationAsConfigured.java
index 0e5cc23..330775e 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/command/CommandFacetForPropertyAnnotationAsConfigured.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/facets/properties/property/command/CommandFacetForPropertyAnnotationAsConfigured.java
@@ -18,8 +18,6 @@
  */
 package org.apache.isis.core.metamodel.facets.properties.property.command;
 
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.services.inject.ServiceInjector;
 import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureId.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureId.java
index 1ef6e8a..cd0b1e9 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureId.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/appfeat/ApplicationFeatureId.java
@@ -454,8 +454,8 @@ public class ApplicationFeatureId implements Comparable<ApplicationFeatureId>, S
     private static final ToString<ApplicationFeatureId> toString =
             ObjectContracts.toString("type", ApplicationFeatureId::getType)
             .thenToString("packageName", ApplicationFeatureId::getPackageName)
-            .thenToStringOmmitIfAbsent("className", ApplicationFeatureId::getClassName)
-            .thenToStringOmmitIfAbsent("memberName", ApplicationFeatureId::getMemberName);
+            .thenToStringOmitIfAbsent("className", ApplicationFeatureId::getClassName)
+            .thenToStringOmitIfAbsent("memberName", ApplicationFeatureId::getMemberName);
 
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/command/CommandDtoServiceInternal.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/command/CommandDtoServiceInternal.java
index b0babce..7742c05 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/command/CommandDtoServiceInternal.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/services/command/CommandDtoServiceInternal.java
@@ -35,35 +35,32 @@ import org.apache.isis.schema.cmd.v2.PropertyDto;
  */
 public interface CommandDtoServiceInternal {
 
-
     /**
-     * Returns a JAXB DTO (hence convertible to XML) that represents the intention to invoke an action on a
-     * target object (or possibly many targets, for bulk actions), or to edit a property.  If an action, it can also
-     * be either mixin action or a contributed action.
+     * @return a DTO that represents the intention to invoke an action on a
+     *         target object (or possibly many targets, for bulk actions),
+     *         or to edit a property.  If an action, it be either a
+     *         mixin action or a contributed action.
      */
-    @Programmatic
     CommandDto asCommandDto(
             final List<ManagedObject> targetAdapters,
             final ObjectAction objectAction,
             final Can<ManagedObject> argAdapters);
 
     /**
-     * Returns a JAXB DTO (hence convertible to XML) that represents the intention to edit (set or clear) a property on
-     * a target (or possibly many targets, for symmetry with actions).
+     * @return a DTO that represents the intention to edit (set or clear) a
+     *         property on a target (or possibly many targets, for symmetry
+     *         with actions).
      */
-    @Programmatic
     CommandDto asCommandDto(
             final List<ManagedObject> targetAdapters,
             final OneToOneAssociation association,
             final ManagedObject valueAdapterOrNull);
 
-    @Programmatic
     void addActionArgs(
             final ObjectAction objectAction,
             final ActionDto actionDto,
             final Can<ManagedObject> argAdapters);
 
-    @Programmatic
     void addPropertyValue(
             final OneToOneAssociation property,
             final PropertyDto propertyDto,
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java
index 98988ad..02d7667 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectActionDefault.java
@@ -30,7 +30,6 @@ import org.apache.isis.applib.RecoverableException;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandContext;
 import org.apache.isis.core.commons.collections.Can;
 import org.apache.isis.core.commons.collections.CanVector;
 import org.apache.isis.core.commons.internal.assertions._Assert;
@@ -45,7 +44,6 @@ import org.apache.isis.core.metamodel.facetapi.FeatureType;
 import org.apache.isis.core.metamodel.facets.FacetedMethod;
 import org.apache.isis.core.metamodel.facets.FacetedMethodParameter;
 import org.apache.isis.core.metamodel.facets.actions.action.invocation.ActionInvocationFacet;
-import org.apache.isis.core.metamodel.facets.actions.action.invocation.CommandUtil;
 import org.apache.isis.core.metamodel.facets.actions.defaults.ActionDefaultsFacet;
 import org.apache.isis.core.metamodel.facets.actions.prototype.PrototypeFacet;
 import org.apache.isis.core.metamodel.facets.actions.semantics.ActionSemanticsFacet;
@@ -65,6 +63,7 @@ import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.core.metamodel.spec.feature.ObjectAction;
 import org.apache.isis.core.metamodel.spec.feature.ObjectActionParameter;
+import org.apache.isis.schema.cmd.v2.CommandDto;
 
 import lombok.NonNull;
 import lombok.val;
@@ -381,13 +380,8 @@ implements ObjectAction {
     }
 
     /**
-     * Sets up the {@link Command}, then delegates off to
-     * {@link #executeInternal(ManagedObject, ManagedObject, List, InteractionInitiatedBy) executeInternal}
+     * Sets up the {@link Command}, then delegates to {@link #executeInternal(InteractionHead, Can, InteractionInitiatedBy)}
      * to invoke the {@link ActionInvocationFacet invocation facet}.
-     *
-     * @param mixedInAdapter - will be null for regular actions, and for mixin actions.  
-     * When a mixin action invokes its underlying mixedIn action, then will be populated 
-     * (so that the ActionDomainEvent can correctly provide the underlying mixin)
      */
     @Override
     public ManagedObject execute(
@@ -578,53 +572,34 @@ implements ObjectAction {
     }
 
     /**
-     * Internal API, called by the various implementations of {@link ObjectAction} ({@link ObjectActionDefault default},
-     * {@link ObjectActionMixedIn mixed-in} and {@link ObjectActionContributee contributee}).
+     * Internal API, called by the various implementations of
+     * {@link ObjectAction} ({@link ObjectActionDefault default} and
+     * {@link ObjectActionMixedIn mixed-in}.
      */
     public void setupCommand(
             final ManagedObject targetAdapter,
             final Can<ManagedObject> argumentAdapters) {
 
-        final CommandContext commandContext = getCommandContext();
-        final Command command = commandContext.getCommand();
-
-        _Assert.assertNotNull(command, "No command available with current thread, "
-                + "are we missing an interaction context?");
-        
-        if (command.getExecutor() != Command.Executor.USER) {
-            return;
-        }
-
-        setupCommandTarget(targetAdapter, argumentAdapters);
-        setupCommandMemberIdentifier();
-        setupCommandMementoAndExecutionContext(targetAdapter, argumentAdapters);
+        setupCommandTarget(targetAdapter);
+        setupCommandLogicalMemberIdentifier();
+        val dto = createCommandDto(targetAdapter, argumentAdapters);
+        setupCommandDtoAndExecutionContext(dto);
     }
 
-    private void setupCommandTarget(
+    private CommandDto createCommandDto(
             final ManagedObject targetAdapter,
             final Can<ManagedObject> argumentAdapters) {
 
-        final String arguments = CommandUtil.argDescriptionFor(this, argumentAdapters.toList());
-        super.setupCommandTarget(targetAdapter, arguments);
-    }
-
-    private void setupCommandMementoAndExecutionContext(
-            final ManagedObject targetAdapter,
-            final Can<ManagedObject> argumentAdapters) {
-
-        val commandDtoServiceInternal = getCommandDtoService();
         final List<ManagedObject> commandTargetAdapters =
                 commandTargetAdaptersHolder.get() != null
                 ? commandTargetAdaptersHolder.get()
                         : Collections.singletonList(targetAdapter);
 
-                val commandDto = commandDtoServiceInternal.asCommandDto(
-                        commandTargetAdapters, this, argumentAdapters);
-
-                setupCommandDtoAndExecutionContext(commandDto);
-
+        return getCommandDtoServiceInternal().asCommandDto(
+                commandTargetAdapters, this, argumentAdapters);
     }
 
+
     // -- toString
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
index 55ee3db..64caed4 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/ObjectMemberAbstract.java
@@ -26,7 +26,7 @@ import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.CommandContext;
-import org.apache.isis.applib.util.schema.CommandDtoUtils;
+import org.apache.isis.core.commons.internal.assertions._Assert;
 import org.apache.isis.core.metamodel.commons.StringExtensions;
 import org.apache.isis.core.metamodel.consent.Consent;
 import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
@@ -36,7 +36,6 @@ import org.apache.isis.core.metamodel.facetapi.FacetHolder;
 import org.apache.isis.core.metamodel.facetapi.FeatureType;
 import org.apache.isis.core.metamodel.facets.FacetedMethod;
 import org.apache.isis.core.metamodel.facets.actions.action.invocation.CommandUtil;
-import org.apache.isis.core.metamodel.facets.actions.command.CommandFacet;
 import org.apache.isis.core.metamodel.facets.all.describedas.DescribedAsFacet;
 import org.apache.isis.core.metamodel.facets.all.help.HelpFacet;
 import org.apache.isis.core.metamodel.facets.all.hide.HiddenFacet;
@@ -309,81 +308,51 @@ implements ObjectMember, MetaModelContext.Delegating, FacetHolder.Delegating {
         return getServiceRegistry().lookupServiceElseFail(CommandContext.class);
     }
 
-    protected CommandDtoServiceInternal getCommandDtoService() {
+    protected CommandDtoServiceInternal getCommandDtoServiceInternal() {
         return getServiceRegistry().lookupServiceElseFail(CommandDtoServiceInternal.class);
     }
 
     // -- command (setup)
 
-
-    protected void setupCommandTarget(final ManagedObject targetAdapter, final String arguments) {
+    protected void setupCommandTarget(final ManagedObject targetAdapter) {
         final CommandContext commandContext = getCommandContext();
         final Command command = commandContext.getCommand();
 
-        if (command.getExecutor() != Command.Executor.USER) {
-            return;
-        }
+        _Assert.assertNotNull(command, "No command available with current thread, "
+                + "are we missing an interaction context?");
 
         if(command.getTarget() != null) {
             // is set up by the outer-most action; inner actions (invoked via the WrapperFactory) must not overwrite
             return;
         }
 
-        command.internal().setTargetClass(CommandUtil.targetClassNameFor(targetAdapter));
-        command.internal().setTargetAction(CommandUtil.targetMemberNameFor(this));
-        command.internal().setArguments(arguments);
-
         final Bookmark targetBookmark = CommandUtil.bookmarkFor(targetAdapter);
         command.internal().setTarget(targetBookmark);
     }
 
-    protected void setupCommandMemberIdentifier() {
+    protected void setupCommandLogicalMemberIdentifier() {
 
-        final CommandContext commandContext = getCommandContext();
-        final Command command = commandContext.getCommand();
-
-        if (command.getExecutor() != Command.Executor.USER) {
-            return;
-        }
+        val command = getCommandContext().getCommand();
 
-        if (command.getMemberIdentifier() != null) {
+        if (command.getLogicalMemberIdentifier() != null) {
             // any contributed/mixin actions will fire after the main action
             // the guard here prevents them from trashing the command's memberIdentifier
             return;
         }
 
-        command.internal().setMemberIdentifier(CommandUtil.memberIdentifierFor(this));
+        command.internal().setLogicalMemberIdentifier(CommandUtil.logicalMemberIdentifierFor(this));
     }
 
     protected void setupCommandDtoAndExecutionContext(final CommandDto dto) {
-        final CommandContext commandContext = getCommandContext();
-        final Command command = commandContext.getCommand();
-
-        if (command.getExecutor() != Command.Executor.USER) {
-            return;
-        }
+        val command = getCommandContext().getCommand();
 
-        if (command.getMemento() != null) {
+        if (command.getCommandDto() != null) {
             // guard here to prevent subsequent contributed/mixin actions from
             // trampling over the command's memento and execution context
             return;
         }
 
-        // memento
-
-        final String mementoXml = CommandDtoUtils.toXml(dto);
-        command.internal().setMemento(mementoXml);
-
-        // copy over the command execution 'context' (if available)
-        final CommandFacet commandFacet = getFacetHolder().getFacet(CommandFacet.class);
-        if(commandFacet != null && !commandFacet.isDisabled()) {
-            //command.internal().setExecuteIn(commandFacet.executeIn());
-            command.internal().setPersistence(commandFacet.persistence());
-        } else {
-            // if no facet, assume do want to execute right now, but only persist (eventually) if hinted.
-            //command.internal().setExecuteIn(org.apache.isis.applib.annotation.CommandExecuteIn.FOREGROUND);
-            command.internal().setPersistence(org.apache.isis.applib.annotation.CommandPersistence.IF_HINTED);
-        }
+        command.internal().setCommandDto(dto);
     }
 
     @Override
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java
index f94919f..272a9c7 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/specloader/specimpl/OneToOneAssociationDefault.java
@@ -31,7 +31,6 @@ import org.apache.isis.core.metamodel.consent.InteractionInitiatedBy;
 import org.apache.isis.core.metamodel.consent.InteractionResult;
 import org.apache.isis.core.metamodel.facetapi.FeatureType;
 import org.apache.isis.core.metamodel.facets.FacetedMethod;
-import org.apache.isis.core.metamodel.facets.actions.action.invocation.CommandUtil;
 import org.apache.isis.core.metamodel.facets.objectvalue.mandatory.MandatoryFacet;
 import org.apache.isis.core.metamodel.facets.param.autocomplete.MinLengthUtil;
 import org.apache.isis.core.metamodel.facets.propcoll.accessor.PropertyOrCollectionAccessorFacet;
@@ -48,7 +47,6 @@ import org.apache.isis.core.metamodel.interactions.PropertyVisibilityContext;
 import org.apache.isis.core.metamodel.interactions.UsabilityContext;
 import org.apache.isis.core.metamodel.interactions.ValidityContext;
 import org.apache.isis.core.metamodel.interactions.VisibilityContext;
-import org.apache.isis.core.metamodel.services.command.CommandDtoServiceInternal;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
 import org.apache.isis.core.metamodel.spec.ManagedObjects.EntityUtil;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
@@ -301,37 +299,15 @@ implements OneToOneAssociation {
             final ManagedObject targetAdapter,
             final ManagedObject valueAdapterOrNull) {
 
-        val currentExecutor = getCommandContext()
-                .getCurrentExecutor()
-                .orElse(Command.Executor.OTHER);
-
-        if (currentExecutor != Command.Executor.USER) {
-            return;
-        }
-
-        setupCommandTarget(targetAdapter, valueAdapterOrNull);
-        setupCommandMemberIdentifier();
-        setupCommandMementoAndExecutionContext(targetAdapter, valueAdapterOrNull);
-    }
-
-    private void setupCommandTarget(
-            final ManagedObject targetAdapter,
-            final ManagedObject valueAdapter) {
-
-        final String arguments = CommandUtil.argDescriptionFor(valueAdapter);
-        setupCommandTarget(targetAdapter, arguments);
+        setupCommandTarget(targetAdapter);
+        setupCommandLogicalMemberIdentifier();
+        val dto = createCommandDto(targetAdapter, valueAdapterOrNull);
+        setupCommandDtoAndExecutionContext(dto);
     }
 
-    private void setupCommandMementoAndExecutionContext(
-            final ManagedObject targetAdapter,
-            final ManagedObject valueAdapterOrNull) {
-
-        final CommandDtoServiceInternal commandDtoServiceInternal = getCommandDtoService();
-        final CommandDto dto = commandDtoServiceInternal.asCommandDto(
+    private CommandDto createCommandDto(ManagedObject targetAdapter, ManagedObject valueAdapterOrNull) {
+        return getCommandDtoServiceInternal().asCommandDto(
                 Collections.singletonList(targetAdapter), this, valueAdapterOrNull);
-
-        setupCommandDtoAndExecutionContext(dto);
-
     }
 
 
diff --git a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueTypeProviderForBuiltin.java b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueTypeProviderForBuiltin.java
index 8d346c0..f9de0c6 100644
--- a/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueTypeProviderForBuiltin.java
+++ b/core/metamodel/src/main/java/org/apache/isis/core/metamodel/valuetypes/ValueTypeProviderForBuiltin.java
@@ -28,6 +28,7 @@ import org.springframework.stereotype.Component;
 import org.apache.isis.applib.annotation.OrderPrecedence;
 import org.apache.isis.applib.graph.SimpleEdge;
 import org.apache.isis.applib.graph.tree.LazyTreeNode;
+import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.core.commons.internal.collections._Lists;
 import org.apache.isis.core.metamodel.services.appfeat.ApplicationFeature;
 import org.apache.isis.schema.common.v2.ValueType;
@@ -41,12 +42,14 @@ public class ValueTypeProviderForBuiltin implements ValueTypeProvider {
     public Collection<ValueTypeDefinition> definitions() {
         return _Lists.of(
                     
+                    ValueTypeDefinition.of(Bookmark.class, ValueType.STRING),
+
                     // these are not yet part of the schema (do not map onto any value-types there)
                     ValueTypeDefinition.of(SimpleEdge.class, ValueType.STRING),
                     ValueTypeDefinition.of(LazyTreeNode.class, ValueType.STRING),
-                    
+
                     ValueTypeDefinition.of(ApplicationFeature.class, ValueType.STRING)
-                    
+
                 );
     }
 
diff --git a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Command.java b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Command.java
index f322a8a..deb5634 100644
--- a/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Command.java
+++ b/core/metamodel/src/test/java/org/apache/isis/core/metamodel/facets/actions/action/ActionAnnotationFacetFactoryTest_Command.java
@@ -29,9 +29,6 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertTrue;
 
 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.annotation.CommandReification;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.core.config.metamodel.facets.CommandActionsConfiguration;
 import org.apache.isis.core.metamodel.facetapi.Facet;
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/background/BackgroundCommandExecution.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/background/BackgroundCommandExecution.java
index 392b4d6..16a9667 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/background/BackgroundCommandExecution.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/background/BackgroundCommandExecution.java
@@ -22,7 +22,6 @@ import java.util.List;
 
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.CommandExecutorService;
-import org.apache.isis.applib.services.command.CommandWithDto;
 import org.apache.isis.core.commons.internal.collections._Lists;
 
 import lombok.val;
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/background/CommandExecutionAbstract.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/background/CommandExecutionAbstract.java
index d56cbc7..113fd7f 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/background/CommandExecutionAbstract.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/background/CommandExecutionAbstract.java
@@ -21,7 +21,6 @@ package org.apache.isis.core.runtimeservices.background;
 import javax.inject.Inject;
 
 import org.apache.isis.applib.services.command.CommandExecutorService;
-import org.apache.isis.applib.services.command.CommandWithDto;
 import org.apache.isis.applib.services.xactn.TransactionService;
 import org.apache.isis.core.runtime.iactn.template.AbstractIsisInteractionTemplate;
 
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoServiceInternalDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoServiceInternalDefault.java
index 12da090..71b5c76 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoServiceInternalDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandDtoServiceInternalDefault.java
@@ -64,8 +64,8 @@ import lombok.val;
 @Qualifier("Default")
 public class CommandDtoServiceInternalDefault implements CommandDtoServiceInternal {
 
-    @Inject private javax.inject.Provider<CommandContext> commandContextProvider;
-    @Inject private BookmarkService bookmarkService;
+    @Inject javax.inject.Provider<CommandContext> commandContextProvider;
+    @Inject BookmarkService bookmarkService;
 
     @Override
     public CommandDto asCommandDto(
@@ -131,13 +131,9 @@ public class CommandDtoServiceInternalDefault implements CommandDtoServiceIntern
             final ObjectAction objectAction,
             final ActionDto actionDto,
             final Can<ManagedObject> argAdapters) {
-        
-        final String actionId = CommandUtil.memberIdentifierFor(objectAction);
-        final ObjectSpecification onType = objectAction.getOnType();
-        final String objectType = onType.getSpecId().asString();
-        final String localId = objectAction.getIdentifier().toNameIdentityString();
-        actionDto.setLogicalMemberIdentifier(objectType + "#" + localId);
-        actionDto.setMemberIdentifier(actionId);
+
+        actionDto.setLogicalMemberIdentifier(CommandUtil.logicalMemberIdentifierFor(objectAction));
+        actionDto.setMemberIdentifier(CommandUtil.memberIdentifierFor(objectAction));
 
         val actionParameters = objectAction.getParameters();
         for (int paramNum = 0; paramNum < actionParameters.size(); paramNum++) {
@@ -161,12 +157,8 @@ public class CommandDtoServiceInternalDefault implements CommandDtoServiceIntern
             final PropertyDto propertyDto,
             final ManagedObject valueAdapter) {
 
-        val actionIdentifier = CommandUtil.memberIdentifierFor(property);
-        val onType = property.getOnType();
-        val objectType = onType.getSpecId().asString();
-        val localId = property.getIdentifier().toNameIdentityString();
-        propertyDto.setLogicalMemberIdentifier(objectType + "#" + localId);
-        propertyDto.setMemberIdentifier(actionIdentifier);
+        propertyDto.setLogicalMemberIdentifier(CommandUtil.logicalMemberIdentifierFor(property));
+        propertyDto.setMemberIdentifier(CommandUtil.memberIdentifierFor(property));
 
         val valueSpec = property.getSpecification();
         val valueType = valueSpec.getCorrespondingClass();
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
index 68fb7a0..ca87863 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandExecutorServiceDefault.java
@@ -40,7 +40,6 @@ import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.applib.services.clock.ClockService;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.CommandExecutorService;
-import org.apache.isis.applib.services.command.CommandWithDto;
 import org.apache.isis.applib.services.iactn.Interaction;
 import org.apache.isis.applib.services.iactn.InteractionContext;
 import org.apache.isis.applib.services.sudo.SudoService;
@@ -105,9 +104,9 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
     @Override
     public void executeCommand(
             final CommandExecutorService.SudoPolicy sudoPolicy,
-            final CommandWithDto commandWithDto) {
+            final Command command) {
 
-        final Runnable commandRunnable = ()->executeCommand(commandWithDto);
+        final Runnable commandRunnable = ()->executeCommand(command);
         final Runnable topLevelRunnable;
 
         switch (sudoPolicy) {
@@ -115,7 +114,7 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
             topLevelRunnable = commandRunnable;
             break;
         case SWITCH:
-            val user = commandWithDto.getUser();
+            val user = command.getUsername();
             topLevelRunnable = ()->sudoService.sudo(user, commandRunnable);
             break;
         default:
@@ -126,27 +125,22 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
 
             transactionService.executeWithinTransaction(topLevelRunnable);
 
-            afterCommit(commandWithDto, /*exception*/null);
+            afterCommit(command, /*exception*/null);
 
         } catch (Exception e) {
 
-            val executeIn = commandWithDto.getExecuteIn();
-
-            log.warn("Exception when executing : {} {}", executeIn, commandWithDto.getMemberIdentifier(), e);
-            afterCommit(commandWithDto, e);
+            log.warn("Exception when executing : {}", command.getLogicalMemberIdentifier(), e);
+            afterCommit(command, e);
         }
 
     }
 
-    protected void executeCommand(final CommandWithDto commandWithDto) {
+    protected void executeCommand(final Command command) {
 
         // setup for us by IsisTransactionManager; will have the transactionId of the backgroundCommand
         val interaction = interactionContextProvider.get().getInteraction();
-        val executeIn = commandWithDto.getExecuteIn();
-
-        log.info("Executing: {} {} {} {}", executeIn, commandWithDto.getMemberIdentifier(), commandWithDto.getTimestamp(), commandWithDto.getUniqueId());
 
-        commandWithDto.internal().setExecutor(Command.Executor.BACKGROUND);
+        log.info("Executing: {} {} {}", command.getLogicalMemberIdentifier(), command.getTimestamp(), command.getUniqueId());
 
         // responsibility for setting the Command#startedAt is in the ActionInvocationFacet or
         // PropertySetterFacet, but this is run if the domain object was found.  If the domain object is
@@ -159,12 +153,12 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
                 ? currentExecution.getStartedAt()
                         : clockService.nowAsJavaSqlTimestamp();
 
-        commandWithDto.internal().setStartedAt(startedAt);
+        command.internal().setStartedAt(startedAt);
 
-        final CommandDto dto = commandWithDto.asDto();
+        val dto = command.getCommandDto();
 
-        Bookmark resultBookmark = executeCommand(dto);
-        commandWithDto.internal().setResult(resultBookmark);
+        val resultBookmark = executeCommand(dto);
+        command.internal().setResult(resultBookmark);
     }
 
     @Override
@@ -233,17 +227,17 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
         return null;
     }
 
-    protected void afterCommit(CommandWithDto commandWithDto, Exception exceptionIfAny) {
+    protected void afterCommit(final Command command, final Exception exceptionIfAny) {
 
         val interaction = interactionContextProvider.get().getInteraction();
 
         // it's possible that there is no priorExecution, specifically if there was an exception
         // when performing the action invocation/property edit.  We therefore need to guard that case.
         final Interaction.Execution<?, ?> priorExecution = interaction.getPriorExecution();
-        if (commandWithDto.getStartedAt() == null) {
+        if (command.getStartedAt() == null) {
             // if attempting to commit the xactn threw an error, we will (I think?) have lost this info, so need to
             // capture
-            commandWithDto.internal().setStartedAt(
+            command.internal().setStartedAt(
                     priorExecution != null
                     ? priorExecution.getStartedAt()
                             : clockService.nowAsJavaSqlTimestamp());
@@ -253,12 +247,10 @@ public class CommandExecutorServiceDefault implements CommandExecutorService {
                 priorExecution != null
                 ? priorExecution.getCompletedAt()
                         : clockService.nowAsJavaSqlTimestamp();  // close enough...
-                commandWithDto.internal().setCompletedAt(completedAt);
+                command.internal().setCompletedAt(completedAt);
 
                 if(exceptionIfAny != null) {
-                    commandWithDto.internal().setException(_Exceptions.
-                            streamStacktraceLines(exceptionIfAny, 500)
-                            .collect(Collectors.joining("\n")));
+                    command.internal().setException(exceptionIfAny);
                 }
 
     }
diff --git a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandServiceDefault.java b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandServiceDefault.java
index d9f3c00..527692f 100644
--- a/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandServiceDefault.java
+++ b/core/runtimeservices/src/main/java/org/apache/isis/core/runtimeservices/command/CommandServiceDefault.java
@@ -27,12 +27,11 @@ 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)
+@Order(OrderPrecedence.LATE)
 @Primary
 @Qualifier("Default")
 public class CommandServiceDefault implements CommandService {
diff --git a/examples/demo/domain/pom.xml b/examples/demo/domain/pom.xml
index 60787ed..4e19164 100644
--- a/examples/demo/domain/pom.xml
+++ b/examples/demo/domain/pom.xml
@@ -109,6 +109,12 @@
             <artifactId>isis-valuetypes-markdown-applib</artifactId>
         </dependency>
 
-    </dependencies>
+		<!-- Extensions -->
+		<dependency>
+			<groupId>org.apache.isis.extensions</groupId>
+			<artifactId>isis-extensions-command-log-impl</artifactId>
+		</dependency>
+
+	</dependencies>
 
 </project>
\ No newline at end of file
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/DemoModule.java b/examples/demo/domain/src/main/java/demoapp/dom/DemoModule.java
index d4b3103..cb7cb7c 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/DemoModule.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/DemoModule.java
@@ -27,6 +27,7 @@ import org.springframework.context.annotation.PropertySources;
 
 import org.apache.isis.core.config.presets.IsisPresets;
 import org.apache.isis.core.runtimeservices.IsisModuleCoreRuntimeServices;
+import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
 import org.apache.isis.extensions.modelannotation.metamodel.IsisModuleExtModelAnnotation;
 import org.apache.isis.extensions.secman.api.SecurityModuleConfig;
 import org.apache.isis.extensions.secman.api.permission.PermissionsEvaluationService;
@@ -38,11 +39,13 @@ import org.apache.isis.testing.fixtures.applib.IsisModuleTestingFixturesApplib;
 @Import({
     IsisModuleCoreRuntimeServices.class,
     IsisModuleJdoDataNucleus5.class,
-    
+
     IsisModuleTestingFixturesApplib.class,
 
     IsisModuleExtModelAnnotation.class, // @Model support
 
+    IsisModuleExtCommandLogImpl.class,
+
 })
 @PropertySources({
     @PropertySource(IsisPresets.H2InMemory),
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandDisabledMetaAnnotation.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandDisabledMetaAnnotation.java
index 5cf894b..21b53f8 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandDisabledMetaAnnotation.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandDisabledMetaAnnotation.java
@@ -7,9 +7,6 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.CommandReification;
-import org.apache.isis.applib.annotation.Property;
-import org.apache.isis.applib.annotation.Publishing;
 
 //tag::class[]
 @Action(command = CommandReification.DISABLED)  // <.>
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.java
index a385c5c..cb9e6bd 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.java
@@ -18,8 +18,6 @@
  */
 package demoapp.dom.annotDomain.Action.command;
 
-import java.util.concurrent.ExecutorService;
-
 import javax.inject.Inject;
 import javax.jdo.annotations.DatastoreIdentity;
 import javax.jdo.annotations.IdGeneratorStrategy;
@@ -28,15 +26,11 @@ import javax.jdo.annotations.PersistenceCapable;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
-import org.apache.isis.applib.annotation.CommandReification;
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.Editing;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.Nature;
 import org.apache.isis.applib.annotation.Property;
-import org.apache.isis.applib.annotation.Publishing;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.wrapper.WrapperFactory;
 import org.apache.isis.applib.services.wrapper.control.AsyncControl;
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml
index 8b34302..0e2cf8c 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/ActionCommandJdo.layout.xml
@@ -28,8 +28,7 @@
 			</bs3:row>
 			<bs3:row>
 				<bs3:col span="12">
-					<cpt:collection id="foregroundCommands"/>
-					<cpt:collection id="backgroundCommands"/>
+					<cpt:collection id="commands"/>
 				</bs3:col>
 			</bs3:row>
 			<bs3:row>
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_backgroundCommands.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_backgroundCommands.java
deleted file mode 100644
index ab695e5..0000000
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_backgroundCommands.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package demoapp.dom.annotDomain.Action.command.spiimpl;
-
-import java.util.LinkedList;
-
-import javax.inject.Inject;
-
-import org.apache.isis.applib.annotation.Collection;
-import org.apache.isis.applib.services.command.CommandWithDto;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-
-import lombok.val;
-
-import demoapp.dom.annotDomain.Action.command.ActionCommandJdo;
-
-//tag::class[]
-@Collection
-public class ActionCommandJdo_backgroundCommands {
-    // ...
-//end::class[]
-
-    private final ActionCommandJdo actionCommandJdo;
-    public ActionCommandJdo_backgroundCommands(ActionCommandJdo actionCommandJdo) {
-        this.actionCommandJdo = actionCommandJdo;
-    }
-
-    //tag::class[]
-    public LinkedList<CommandDto> coll() {
-        val list = new LinkedList<CommandDto>();
-        commandServiceSpiForActions
-                .streamBackgroundCommands()
-                .map(CommandWithDto::asDto)
-                .forEach(list::push);   // reverse order
-        return list;
-    }
-
-    @Inject
-    private CommandServiceSpiForActions commandServiceSpiForActions;
-}
-//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_clearBackgroundCommands.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_clearBackgroundCommands.java
deleted file mode 100644
index 410674f..0000000
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_clearBackgroundCommands.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package demoapp.dom.annotDomain.Action.command.spiimpl;
-
-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 demoapp.dom.annotDomain.Action.command.ActionCommandJdo;
-
-//tag::class[]
-@Action(
-    semantics = SemanticsOf.IDEMPOTENT
-    , associateWith = "backgroundCommands"
-)
-@ActionLayout(
-    named = "Clear"
-)
-public class ActionCommandJdo_clearBackgroundCommands {
-    // ...
-//end::class[]
-
-    private final ActionCommandJdo actionCommandJdo;
-    public ActionCommandJdo_clearBackgroundCommands(ActionCommandJdo actionCommandJdo) {
-        this.actionCommandJdo = actionCommandJdo;
-    }
-
-
-    //tag::class[]
-    public ActionCommandJdo act() {
-        commandServiceSpiForActions.clearBackgroundCommands();
-        return actionCommandJdo;
-    }
-
-    @Inject
-    private CommandServiceSpiForActions commandServiceSpiForActions;
-}
-//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_clearForegroundCommands.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_clearForegroundCommands.java
deleted file mode 100644
index 3485421..0000000
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_clearForegroundCommands.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package demoapp.dom.annotDomain.Action.command.spiimpl;
-
-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 demoapp.dom.annotDomain.Action.command.ActionCommandJdo;
-
-//tag::class[]
-@Action(
-    semantics = SemanticsOf.IDEMPOTENT
-    , associateWith = "foregroundCommands"
-)
-@ActionLayout(
-    named = "Clear"
-)
-public class ActionCommandJdo_clearForegroundCommands {
-    // ...
-//end::class[]
-
-    private final ActionCommandJdo actionCommandJdo;
-    public ActionCommandJdo_clearForegroundCommands(ActionCommandJdo actionCommandJdo) {
-        this.actionCommandJdo = actionCommandJdo;
-    }
-
-
-    //tag::class[]
-    public ActionCommandJdo act() {
-        commandServiceSpiForActions.clearForegroundCommands();
-        return actionCommandJdo;
-    }
-
-    @Inject
-    private CommandServiceSpiForActions commandServiceSpiForActions;
-}
-//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_foregroundCommands.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_commands.java
similarity index 63%
rename from examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_foregroundCommands.java
rename to examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_commands.java
index 5c57292..c3fb8c2 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_foregroundCommands.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_commands.java
@@ -5,7 +5,8 @@ import java.util.LinkedList;
 import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Collection;
-import org.apache.isis.applib.services.command.CommandWithDto;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
 import lombok.val;
@@ -14,26 +15,25 @@ import demoapp.dom.annotDomain.Action.command.ActionCommandJdo;
 
 //tag::class[]
 @Collection
-public class ActionCommandJdo_foregroundCommands {
+public class ActionCommandJdo_commands {
     // ...
 //end::class[]
 
     private final ActionCommandJdo actionCommandJdo;
-    public ActionCommandJdo_foregroundCommands(ActionCommandJdo actionCommandJdo) {
+    public ActionCommandJdo_commands(ActionCommandJdo actionCommandJdo) {
         this.actionCommandJdo = actionCommandJdo;
     }
 
     //tag::class[]
     public LinkedList<CommandDto> coll() {
         val list = new LinkedList<CommandDto>();
-        commandServiceSpiForActions
-                .streamForegroundCommands()
-                .map(CommandWithDto::asDto)
+        commandJdoRepository.findCompleted()
+                .stream().map(CommandJdo::getCommandDto)
                 .forEach(list::push);   // reverse order
         return list;
     }
 
     @Inject
-    private CommandServiceSpiForActions commandServiceSpiForActions;
+    CommandJdoRepository commandJdoRepository;
 }
 //end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_executeBackgroundCommands.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_executeBackgroundCommands.java
deleted file mode 100644
index 43b0dbc..0000000
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/ActionCommandJdo_executeBackgroundCommands.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package demoapp.dom.annotDomain.Action.command.spiimpl;
-
-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 demoapp.dom.annotDomain.Action.command.ActionCommandJdo;
-
-//tag::class[]
-@Action(
-    semantics = SemanticsOf.IDEMPOTENT
-    , associateWith = "backgroundCommands"
-    , associateWithSequence = "2"
-)
-@ActionLayout(
-    named = "Execute"
-)
-public class ActionCommandJdo_executeBackgroundCommands {
-    // ...
-//end::class[]
-
-    private final ActionCommandJdo actionCommandJdo;
-    public ActionCommandJdo_executeBackgroundCommands(ActionCommandJdo actionCommandJdo) {
-        this.actionCommandJdo = actionCommandJdo;
-    }
-
-
-    //tag::class[]
-    public ActionCommandJdo act() {
-        commandServiceSpiForActions.clearBackgroundCommands();
-        return actionCommandJdo;
-    }
-
-    @Inject
-    private CommandServiceSpiForActions commandServiceSpiForActions;
-}
-//end::class[]
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/CommandServiceSpiForActions.java b/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/CommandServiceSpiForActions.java
deleted file mode 100644
index af62cf8..0000000
--- a/examples/demo/domain/src/main/java/demoapp/dom/annotDomain/Action/command/spiimpl/CommandServiceSpiForActions.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package demoapp.dom.annotDomain.Action.command.spiimpl;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Stream;
-
-import javax.inject.Inject;
-
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.services.background.BackgroundCommandService;
-import org.apache.isis.applib.services.clock.ClockService;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandDefault;
-import org.apache.isis.applib.services.command.CommandWithDto;
-import org.apache.isis.applib.services.command.spi.CommandService;
-import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.core.runtimeservices.background.BackgroundCommandExecution;
-import org.apache.isis.core.security.authentication.standard.SimpleSession;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-
-import lombok.val;
-
-@Service
-@Order(OrderPrecedence.EARLY)       // <.>
-public class CommandServiceSpiForActions implements CommandService {
-
-    public static class CommandWithDtoDefault extends CommandDefault implements CommandWithDto {
-        @Override
-        public CommandDto asDto() {
-            return CommandDtoUtils.fromXml(getMemento());
-        }
-    }
-
-    private List<CommandWithDto> foregroundCommands = new ArrayList<>();
-    private List<CommandWithDto> backgroundCommands = new ArrayList<>();
-
-    @Override
-    public Command create() {
-        return new CommandWithDtoDefault();
-    }
-
-    @Override
-    public boolean persistIfPossible(Command command) {
-        return true;
-    }
-
-    @Override
-    public void complete(Command command) {
-        final CommandWithDtoDefault cwdd = (CommandWithDtoDefault) command;
-        if(command.isPersistHint()) {
-            final CommandExecuteIn executeIn = command.getExecuteIn();
-            switch (executeIn) {
-                case FOREGROUND:
-                    foregroundCommands.add((CommandWithDto) command);
-                case BACKGROUND:
-                    backgroundCommands.add((CommandWithDto) command);
-            }
-        }
-    }
-
-    public Stream<CommandWithDto> streamForegroundCommands() {
-        return foregroundCommands.stream();
-    }
-
-    public void clearForegroundCommands() {
-        foregroundCommands.clear();
-    }
-
-    public Stream<CommandWithDto> streamBackgroundCommands() {
-        return backgroundCommands.stream();
-    }
-
-    public void clearBackgroundCommands() {
-        backgroundCommands.clear();
-    }
-
-    public void executeBackgroundCommands() {
-        val execution = new BackgroundCommandExecution() {
-            @Override
-            protected List<? extends Command> findBackgroundCommandsToExecute() {
-                return backgroundCommands;
-            }
-        };
-        val simpleSession = new SimpleSession("sven", Collections.emptyList(), "");
-
-        execution.execute(simpleSession, null);
-        clearBackgroundCommands();
-    }
-
-}
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/IsisModuleExtCommandLogImpl.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/IsisModuleExtCommandLogImpl.java
index 991b53d..21135fa 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/IsisModuleExtCommandLogImpl.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/IsisModuleExtCommandLogImpl.java
@@ -4,19 +4,13 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
-import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 import org.apache.isis.extensions.commandlog.impl.ui.CommandServiceMenu;
 import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
 import org.apache.isis.testing.fixtures.applib.modules.ModuleWithFixtures;
 import org.apache.isis.testing.fixtures.applib.teardown.TeardownFixtureAbstract;
 
-import org.apache.isis.extensions.commandlog.impl.background.BackgroundCommandExecutionFromBackgroundCommandServiceJdo;
-import org.apache.isis.extensions.commandlog.impl.background.BackgroundCommandServiceJdo;
-import org.apache.isis.extensions.commandlog.impl.background.BackgroundCommandServiceJdoRepository;
-
 @Configuration
 @Import({
         // @DomainService's
@@ -24,7 +18,7 @@ import org.apache.isis.extensions.commandlog.impl.background.BackgroundCommandSe
         , BackgroundCommandServiceJdo.class
         , BackgroundCommandServiceJdoRepository.class
         , CommandServiceJdo.class
-        , CommandServiceJdoRepository.class
+        , CommandJdoRepository.class
         , CommandServiceMenu.class
 })
 @ComponentScan(
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandWithDto.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/api/UserDataKeys.java
similarity index 64%
rename from api/applib/src/main/java/org/apache/isis/applib/services/command/CommandWithDto.java
rename to extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/api/UserDataKeys.java
index cd421ea..d603b90 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/command/CommandWithDto.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/api/UserDataKeys.java
@@ -16,20 +16,19 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.applib.services.command;
+package org.apache.isis.extensions.commandlog.impl.api;
 
-import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
-// tag::refguide[]
-public interface CommandWithDto extends Command {
+import lombok.experimental.UtilityClass;
 
-    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";
+/**
+ * Keys used in {@link CommandDto#getUserData()} to marshall the command's results
+ */
+@UtilityClass
+public class UserDataKeys {
+
+    public static String RESULT = "org.apache.isis.extensions.commandlog.impl.api.UserDataKeys#RESULT";
+    public static String EXCEPTION = "org.apache.isis.extensions.commandlog.impl.api.UserDataKeys#EXCEPTION";
 
-    CommandDto asDto();
 }
-// end::refguide[]
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/background/BackgroundCommandExecutionFromBackgroundCommandServiceJdo.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/background/BackgroundCommandExecutionFromBackgroundCommandServiceJdo.java
deleted file mode 100644
index dc0d1fe..0000000
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/background/BackgroundCommandExecutionFromBackgroundCommandServiceJdo.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.apache.isis.extensions.commandlog.impl.background;
-
-import java.util.List;
-
-import javax.inject.Inject;
-
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandExecutorService;
-import org.apache.isis.core.runtimeservices.background.BackgroundCommandExecution;
-
-import lombok.extern.log4j.Log4j2;
-
-@DomainService
-@Log4j2
-public class BackgroundCommandExecutionFromBackgroundCommandServiceJdo
-        extends BackgroundCommandExecution {
-
-    public BackgroundCommandExecutionFromBackgroundCommandServiceJdo() {
-        super(CommandExecutorService.SudoPolicy.NO_SWITCH);
-    }
-
-    @Override
-    protected List<? extends Command> findBackgroundCommandsToExecute() {
-        return backgroundCommandRepository.findBackgroundCommandsNotYetStarted();
-    }
-
-    @Inject BackgroundCommandServiceJdoRepository backgroundCommandRepository;
-}
\ No newline at end of file
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/background/BackgroundCommandServiceJdo.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/background/BackgroundCommandServiceJdo.java
deleted file mode 100644
index 637440e..0000000
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/background/BackgroundCommandServiceJdo.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package org.apache.isis.extensions.commandlog.impl.background;
-
-import java.util.UUID;
-
-import javax.inject.Inject;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.services.background.BackgroundCommandService;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.clock.ClockService;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.factory.FactoryService;
-import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-import org.apache.isis.schema.common.v2.OidDto;
-
-import lombok.extern.log4j.Log4j2;
-
-/**
- * Persists a memento-ized action such that it can be executed asynchronously,
- * for example through a Quartz scheduler (using
- * {@link BackgroundCommandExecutionFromBackgroundCommandServiceJdo}).
- */
-@DomainService
-@Log4j2
-public class BackgroundCommandServiceJdo implements BackgroundCommandService {
-
-    @Override
-    public void schedule(
-            final CommandDto dto,
-            final Command parentCommand,
-            final String targetClassName,
-            final String targetActionName,
-            final String targetArgs) {
-
-        final CommandJdo backgroundCommand =
-                newBackgroundCommand(parentCommand, targetClassName, targetActionName, targetArgs);
-
-        final OidDto firstTarget = dto.getTargets().getOid().get(0);
-        backgroundCommand.setTargetStr(Bookmark.from(firstTarget).toString());
-        backgroundCommand.internal().setMemento(CommandDtoUtils.toXml(dto));
-        backgroundCommand.setMemberIdentifier(dto.getMember().getMemberIdentifier());
-
-        commandServiceJdoRepository.persist(backgroundCommand);
-    }
-
-    private CommandJdo newBackgroundCommand(
-            final Command parentCommand,
-            final String targetClassName,
-            final String targetActionName,
-            final String targetArgs) {
-
-        final CommandJdo backgroundCommand = factoryService.instantiate(CommandJdo.class);
-
-        backgroundCommand.internal().setParent(parentCommand);
-
-        // workaround for ISIS-1472; parentCommand not properly set up if invoked via RO viewer
-        if(parentCommand.getMemberIdentifier() == null) {
-            backgroundCommand.internal().setParent(null);
-        }
-
-        final UUID transactionId = UUID.randomUUID();
-        final String user = parentCommand.getUser();
-
-        backgroundCommand.setTransactionId(transactionId);
-
-        backgroundCommand.internal().setUser(user);
-        backgroundCommand.internal().setTimestamp(clockService.nowAsJavaSqlTimestamp());
-        backgroundCommand.internal().setExecuteIn(CommandExecuteIn.BACKGROUND);
-
-        backgroundCommand.setTargetClass(targetClassName);
-        backgroundCommand.setTargetAction(targetActionName);
-
-        backgroundCommand.internal().setArguments(targetArgs);
-        backgroundCommand.internal().setPersistHint(true);
-
-        return backgroundCommand;
-    }
-
-
-    @Inject CommandServiceJdoRepository commandServiceJdoRepository;
-    @Inject FactoryService factoryService;
-    @Inject ClockService clockService;
-
-}
-
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/background/BackgroundCommandServiceJdoRepository.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/background/BackgroundCommandServiceJdoRepository.java
deleted file mode 100644
index 2195292..0000000
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/background/BackgroundCommandServiceJdoRepository.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.apache.isis.extensions.commandlog.impl.background;
-
-import java.util.List;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.core.annotation.Order;
-import org.springframework.stereotype.Service;
-
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.annotation.Programmatic;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
-
-import lombok.extern.log4j.Log4j2;
-
-/**
- * Provides supporting functionality for querying
- * {@link CommandJdo command} entities that have been persisted
- * to execute in the background.
- *
- * <p>
- * This supporting service with no UI and no side-effects, and is there are no other implementations of the service,
- * thus has been annotated with {@link org.apache.isis.applib.annotation.DomainService}.  This means that there is no
- * need to explicitly register it as a service (eg in <tt>isis.properties</tt>).
- */
-@Service()
-@Named("isisExtensionsCommandLog.BackgroundCommandServiceJdoRepository")
-@Order(OrderPrecedence.MIDPOINT)
-@Qualifier("Jdo")
-@Log4j2
-public class BackgroundCommandServiceJdoRepository {
-
-    public List<CommandJdo> findByParent(CommandJdo parent) {
-        return commandServiceRepository.findBackgroundCommandsByParent(parent);
-    }
-
-    public List<CommandJdo> findBackgroundCommandsNotYetStarted() {
-        return commandServiceRepository.findBackgroundCommandsNotYetStarted();
-    }
-
-    @Inject CommandServiceJdoRepository commandServiceRepository;
-}
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo.java
index 2862b06..7707434 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo.java
@@ -1,67 +1,89 @@
+/*
+ *  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.extensions.commandlog.impl.jdo;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.sql.Timestamp;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
+import java.util.Objects;
 import java.util.UUID;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
 
+import javax.inject.Inject;
 import javax.jdo.annotations.IdentityType;
 
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import org.apache.isis.applib.annotation.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.DomainObjectLayout;
 import org.apache.isis.applib.annotation.Editing;
-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.Where;
-import org.apache.isis.applib.services.DomainChangeAbstract;
-import org.apache.isis.applib.services.HasUsername;
+import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
+import org.apache.isis.applib.services.DomainChangeRecord;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandWithDto;
 import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.types.MemberIdentifierType;
-import org.apache.isis.applib.types.TargetActionType;
-import org.apache.isis.applib.types.TargetClassType;
+import org.apache.isis.applib.util.ObjectContracts;
 import org.apache.isis.applib.util.TitleBuffer;
+import org.apache.isis.core.commons.internal.exceptions._Exceptions;
 import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
-import org.apache.isis.extensions.commandlog.impl.jdo.ReplayState;
+import org.apache.isis.extensions.commandlog.impl.api.UserDataKeys;
 import org.apache.isis.schema.cmd.v2.CommandDto;
+import org.apache.isis.schema.cmd.v2.MapDto;
 
 import lombok.Getter;
 import lombok.Setter;
+import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
+/**
+ * A persistent representation of a {@link Command}.
+ *
+ * <p>
+ *     Use cases requiring persistence including auditing, and for replay of
+ *     commands for regression testing purposes.
+ * </p>
+ *
+ * Note that this class doesn't subclass from {@link Command} ({@link Command}
+ * is not an interface).
+ */
 @javax.jdo.annotations.PersistenceCapable(
         identityType=IdentityType.APPLICATION,
-        schema = "isisextcommandlog",
+        schema = "isisExtensionsCommandLog",
         table = "Command")
 @javax.jdo.annotations.Queries( {
     @javax.jdo.annotations.Query(
-            name="findByTransactionId",
+            name="findByUniqueId",
             value="SELECT "
                     + "FROM org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo "
-                    + "WHERE transactionId == :transactionId "),
+                    + "WHERE uniqueId == :uniqueId "),
     @javax.jdo.annotations.Query(
-            name="findBackgroundCommandsByParent",
+            name="findByParent",
             value="SELECT "
                     + "FROM org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo "
-                    + "WHERE parent == :parent "
-                    + "&& executeIn == 'BACKGROUND'"),
+                    + "WHERE parent == :parent "),
     @javax.jdo.annotations.Query(
             name="findCurrent",
             value="SELECT "
@@ -76,12 +98,11 @@ import lombok.extern.log4j.Log4j2;
                     + "&& executeIn == 'FOREGROUND' "
                     + "ORDER BY this.timestamp DESC"),
     @javax.jdo.annotations.Query(
-            name="findRecentBackgroundByTarget",
+            name="findRecentByTarget",
             value="SELECT "
                     + "FROM org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo "
                     + "WHERE targetStr == :targetStr "
-                    + "&& executeIn == 'BACKGROUND' "
-                    + "ORDER BY this.timestamp DESC, transactionId DESC "
+                    + "ORDER BY this.timestamp DESC, uniqueId DESC "
                     + "RANGE 0,30"),
     @javax.jdo.annotations.Query(
             name="findByTargetAndTimestampBetween",
@@ -143,18 +164,10 @@ import lombok.extern.log4j.Log4j2;
                     + "ORDER BY this.timestamp DESC "
                     + "RANGE 0,30"),
     @javax.jdo.annotations.Query(
-            name="findRecentByTarget",
-            value="SELECT "
-                    + "FROM org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo "
-                    + "WHERE targetStr == :targetStr "
-                    + "ORDER BY this.timestamp DESC, transactionId DESC "
-                    + "RANGE 0,30"),
-    @javax.jdo.annotations.Query(
-            name="findForegroundFirst",
+            name="findFirst",
             value="SELECT "
                     + "FROM org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo "
-                    + "WHERE executeIn == 'FOREGROUND' "
-                    + "   && timestamp   != null "
+                    + "WHERE timestamp   != null "
                     + "   && startedAt   != null "
                     + "   && completedAt != null "
                     + "ORDER BY this.timestamp ASC "
@@ -162,11 +175,10 @@ import lombok.extern.log4j.Log4j2;
         // this should be RANGE 0,1 but results in DataNucleus submitting "FETCH NEXT ROW ONLY"
         // which SQL Server doesn't understand.  However, as workaround, SQL Server *does* understand FETCH NEXT 2 ROWS ONLY
     @javax.jdo.annotations.Query(
-            name="findForegroundSince",
+            name="findSince",
             value="SELECT "
                     + "FROM org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo "
-                    + "WHERE executeIn == 'FOREGROUND' "
-                    + "   && timestamp > :timestamp "
+                    + "WHERE timestamp > :timestamp "
                     + "   && startedAt != null "
                     + "   && completedAt != null "
                     + "ORDER BY this.timestamp ASC"),
@@ -226,11 +238,8 @@ import lombok.extern.log4j.Log4j2;
 )
 @DomainObjectLayout(named = "Command")
 @Log4j2
-public class CommandJdo extends DomainChangeAbstract
-        implements Command, CommandWithDto, HasUsername, Comparable<CommandJdo> {
-
-    @SuppressWarnings("unused")
-    private static final Logger LOG = LoggerFactory.getLogger(CommandJdo.class);
+public class CommandJdo
+        implements DomainChangeRecord, Comparable<CommandJdo> {
 
 
     public static abstract class PropertyDomainEvent<T> extends IsisModuleExtCommandLogImpl.PropertyDomainEvent<CommandJdo, T> { }
@@ -238,95 +247,133 @@ public class CommandJdo extends DomainChangeAbstract
     public static abstract class ActionDomainEvent extends IsisModuleExtCommandLogImpl.ActionDomainEvent<CommandJdo> { }
 
     public CommandJdo() {
-        super(DomainChangeAbstract.ChangeType.COMMAND);
-        this.uniqueId = UUID.randomUUID();
+        this(UUID.randomUUID());
+    }
+
+    private CommandJdo(final UUID uniqueId) {
+        super();
+        this.uniqueId = uniqueId;
+    }
+
+    /**
+     * Intended for use on primary system.
+     *
+     * @param command
+     * @param commandJdoRepository
+     */
+    public CommandJdo(
+            final Command command
+            , final CommandJdoRepository commandJdoRepository) {
+        this();
+
+        setUniqueId(command.getUniqueId());
+        setUsername(command.getUsername());
+        setTimestamp(command.getTimestamp());
+
+        setCommandDto(command.getCommandDto());
+        setTarget(command.getTarget());
+        setLogicalMemberIdentifier(command.getLogicalMemberIdentifier());
+
+        val parent = command.getParent();
+        if(parent != null) {
+            setParent(commandJdoRepository.findByUniqueId(parent.getUniqueId()).orElse(null));
+        }
+
+        setStartedAt(command.getStartedAt());
+        setCompletedAt(command.getCompletedAt());
+
+        setResult(command.getResult());
+
+        setException(command.getException());
+
+        setReplayState(ReplayState.UNDEFINED);
+    }
+
+
+    /**
+     * Intended for use on secondary (replay) system.
+     *
+     * @param commandDto - obtained from the primary system as a representation of a command invocation
+     * @param replayState - controls whether this is to be replayed
+     * @param targetIndex - if the command represents a bulk action, then it is flattened out when replayed; this indicates which target to execute against.
+     */
+    public CommandJdo(final CommandDto commandDto, final ReplayState replayState, final int targetIndex) {
+        this();
+
+        setUniqueId(UUID.fromString(commandDto.getTransactionId()));
+        setUsername(commandDto.getUser());
+        setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimestamp()));
+
+        setCommandDto(commandDto);
+        setTarget(Bookmark.from(commandDto.getTargets().getOid().get(targetIndex)));
+        setLogicalMemberIdentifier(commandDto.getMember().getLogicalMemberIdentifier());
+
+        // the hierarchy of commands calling other commands is only available on the primary system, and is
+        setParent(null);
+
+        setStartedAt(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimings().getStartedAt()));
+        setCompletedAt(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimings().getCompletedAt()));
+
+        set(commandDto, UserDataKeys.RESULT, value -> setResult(Bookmark.parse(value).orElse(null)));
+        set(commandDto, UserDataKeys.EXCEPTION, this::setException);
+
+        setReplayState(replayState);
     }
 
+    private void set(CommandDto commandDto, String key, Consumer<String> consumer) {
+        commandDto.getUserData().getEntry()
+                .stream()
+                .filter(x -> Objects.equals(x.getKey(), UserDataKeys.RESULT))
+                .map(MapDto.Entry::getValue)
+                .findFirst()
+                .ifPresent(consumer::accept);
+    }
 
     public String title() {
         // nb: not thread-safe
         // formats defined in https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
-        final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+        val format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
 
-        final TitleBuffer buf = new TitleBuffer();
+        val buf = new TitleBuffer();
         buf.append(format.format(getTimestamp()));
-        buf.append(" ").append(getMemberIdentifier());
+        buf.append(" ").append(getLogicalMemberIdentifier());
         return buf.toString();
     }
 
 
     public static class UniqueIdDomainEvent extends PropertyDomainEvent<UUID> { }
     @javax.jdo.annotations.Persistent
-    @javax.jdo.annotations.Column(allowsNull="false")
-    private UUID uniqueId;
-    /**
-     * {@inheritDoc}
-     */
+    @javax.jdo.annotations.Column(allowsNull="false", length = 36)
     @Property(domainEvent = UniqueIdDomainEvent.class)
-    @Override
-    public UUID getUniqueId() {
-        return uniqueId;
-    }
+    @Getter @Setter
+    private UUID uniqueId;
 
 
-    public static class UserDomainEvent extends PropertyDomainEvent<String> { }
+    public static class UsernameDomainEvent extends PropertyDomainEvent<String> { }
     @javax.jdo.annotations.Column(allowsNull="false", length = 50)
+    @Property(domainEvent = UsernameDomainEvent.class)
+    @Getter @Setter
     private String username;
-    /**
-     * {@inheritDoc}
-     */
-    @Property(domainEvent = UserDomainEvent.class)
-    @Override
-    public String getUsername() {
-        return username;
-    }
 
 
     public static class TimestampDomainEvent extends PropertyDomainEvent<Timestamp> { }
     @javax.jdo.annotations.Persistent
     @javax.jdo.annotations.Column(allowsNull="false")
-    private Timestamp timestamp;
-    /**
-     * {@inheritDoc}
-     */
     @Property(domainEvent = TimestampDomainEvent.class)
-    @Override
-    public Timestamp getTimestamp() {
-        return timestamp;
-    }
+    @Getter @Setter
+    private Timestamp timestamp;
 
 
-    public static class ExecutorDomainEvent extends PropertyDomainEvent<Executor> { }
-    @javax.jdo.annotations.NotPersistent
-    private Executor executor;
-    /**
-     * {@inheritDoc}
-     */
-    @Property(domainEvent = ExecutorDomainEvent.class)
-    @Override
-    public Executor getExecutor() {
-        return executor;
-    }
 
-
-    public static class ExecuteInDomainEvent extends PropertyDomainEvent<CommandExecuteIn> { }
-    @javax.jdo.annotations.Column(allowsNull="false", length = CommandExecuteIn.Type.Meta.MAX_LEN)
-    private CommandExecuteIn executeIn;
-    /**
-     * {@inheritDoc}
-     */
-    @Property(domainEvent = ExecuteInDomainEvent.class)
     @Override
-    public CommandExecuteIn getExecuteIn() {
-        return executeIn;
+    public ChangeType getType() {
+        return ChangeType.COMMAND;
     }
 
 
     public static class ReplayStateDomainEvent extends PropertyDomainEvent<ReplayState> { }
     /**
      * For a replayed command, what the outcome was.
-     *
-     * NOT API.
      */
     @javax.jdo.annotations.Column(allowsNull="true", length=10)
     @Property(domainEvent = ReplayStateDomainEvent.class)
@@ -337,8 +384,6 @@ public class CommandJdo extends DomainChangeAbstract
     public static class ReplayStateFailureReasonDomainEvent extends PropertyDomainEvent<ReplayState> { }
     /**
      * For a {@link ReplayState#FAILED failed} replayed command, what the reason was for the failure.
-     *
-     * <b>NOT API</b>.
      */
     @javax.jdo.annotations.Column(allowsNull="true", length=255)
     @Property(domainEvent = ReplayStateFailureReasonDomainEvent.class)
@@ -352,179 +397,57 @@ public class CommandJdo extends DomainChangeAbstract
 
     public static class ParentDomainEvent extends PropertyDomainEvent<Command> { }
     @javax.jdo.annotations.Persistent
-    @javax.jdo.annotations.Column(name="parentTransactionId", allowsNull="true")
-    private Command parent;
-    /**
-     * {@inheritDoc}
-     */
+    @javax.jdo.annotations.Column(name="parentId", allowsNull="true")
     @Property(domainEvent = ParentDomainEvent.class)
     @PropertyLayout(hidden = Where.ALL_TABLES)
-    @Override
-    public Command getParent() {
-        return parent;
-    }
-
-
-    public static class TransactionIdDomainEvent extends PropertyDomainEvent<UUID> { }
-    @javax.jdo.annotations.PrimaryKey
-    @javax.jdo.annotations.Column(allowsNull="false", length = 36)
-    @Setter
-    private UUID transactionId;
-    /**
-     * {@inheritDoc}
-     *
-     * <p>
-     * Implementation notes: copied over from the Isis transaction when the command is persisted.
-     */
-    @Property(domainEvent = TransactionIdDomainEvent.class)
-    @Override
-    public UUID getTransactionId() {
-        return transactionId;
-    }
-
-
-    public static class TargetClassDomainEvent extends PropertyDomainEvent<String> { }
-    @javax.jdo.annotations.Column(allowsNull="false", length = TargetClassType.Meta.MAX_LEN)
-    private String targetClass;
-    /**
-     * {@inheritDoc}
-     */
-    @Property(domainEvent = TargetClassDomainEvent.class)
-    @PropertyLayout(named="Class")
-    @Override
-    public String getTargetClass() {
-        return targetClass;
-    }
-    public void setTargetClass(final String targetClass) {
-        this.targetClass = abbreviated(targetClass, TargetClassType.Meta.MAX_LEN);
-    }
-
-
-    public static class TargetActionDomainEvent extends PropertyDomainEvent<String> { }
-    @javax.jdo.annotations.Column(allowsNull="false", length = TargetActionType.Meta.MAX_LEN)
-    private String targetAction;
-    /**
-     * {@inheritDoc}
-     */
-    @Property(domainEvent = TargetActionDomainEvent.class, optionality = Optionality.MANDATORY)
-    @PropertyLayout(hidden = Where.NOWHERE, named = "Action")
-    @Override
-    public String getTargetAction() {
-        return targetAction;
-    }
-    public void setTargetAction(final String targetAction) {
-        this.targetAction = abbreviated(targetAction, TargetActionType.Meta.MAX_LEN);
-    }
+    @Getter @Setter
+    private CommandJdo parent;
 
 
-    public static class TargetStrDomainEvent extends PropertyDomainEvent<String> { }
+    public static class TargetDomainEvent extends PropertyDomainEvent<String> { }
+    @javax.jdo.annotations.Persistent
     @javax.jdo.annotations.Column(allowsNull="true", length = 2000, name="target")
-    private String targetStr;
-    /**
-     * {@inheritDoc}
-     */
-    @Property(domainEvent = TargetStrDomainEvent.class)
+    @Property(domainEvent = TargetDomainEvent.class)
     @PropertyLayout(hidden = Where.REFERENCES_PARENT, named = "Object")
-    @Override
-    public String getTargetStr() {
-        return targetStr;
-    }
-    @Override
-    public void setTargetStr(String targetStr) {
-        this.targetStr = targetStr;
-    }
+    @Getter @Setter
+    private Bookmark target;
 
-    public static class ArgumentsDomainEvent extends PropertyDomainEvent<String> { }
-    @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB", sqlType="LONGVARCHAR")
-    private String arguments;
-    /**
-     * {@inheritDoc}
-     */
-    @Property(domainEvent = ArgumentsDomainEvent.class)
-    @PropertyLayout(multiLine = 7, hidden = Where.ALL_TABLES)
     @Override
-    public String getArguments() {
-        return arguments;
+    public String getTargetMember() {
+        return getCommandDto().getMember().getLogicalMemberIdentifier();
     }
 
-
-    public static class MemberIdentifierDomainEvent extends PropertyDomainEvent<String> { }
-    @javax.jdo.annotations.Column(allowsNull="false", length = MemberIdentifierType.Meta.MAX_LEN)
-    private String memberIdentifier;
-    /**
-     * {@inheritDoc}
-     */
-    @Property(domainEvent = MemberIdentifierDomainEvent.class)
+    public static class LogicalMemberIdentifierDomainEvent extends PropertyDomainEvent<String> { }
+    @Property(domainEvent = LogicalMemberIdentifierDomainEvent.class)
     @PropertyLayout(hidden = Where.ALL_TABLES)
-    @Override
-    public String getMemberIdentifier() {
-        return memberIdentifier;
-    }
-    public void setMemberIdentifier(final String memberIdentifier) {
-        this.memberIdentifier = abbreviated(memberIdentifier, MemberIdentifierType.Meta.MAX_LEN);
-    }
+    @javax.jdo.annotations.Column(allowsNull="false", length = MemberIdentifierType.Meta.MAX_LEN)
+    @Getter @Setter
+    private String logicalMemberIdentifier;
 
 
-    public static class MementoDomainEvent extends PropertyDomainEvent<String> { }
+    public static class CommandDtoDomainEvent extends PropertyDomainEvent<CommandDto> { }
+    @javax.jdo.annotations.Persistent
     @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB")
-    private String memento;
-    /**
-     * {@inheritDoc}
-     */
-    @Property(domainEvent = MementoDomainEvent.class)
-    @PropertyLayout(multiLine = 9, hidden = Where.ALL_TABLES)
-    @Override
-    public String getMemento() {
-        return memento;
-    }
-
-
-    // locally cached
-    private transient CommandDto commandDto;
-
-    @Override
-    public CommandDto asDto() {
-        if(commandDto == null) {
-            this.commandDto = buildCommandDto();
-        }
-        return this.commandDto;
-    }
-
-    private CommandDto buildCommandDto() {
-        if(getMemento() == null) {
-            return null;
-        }
-
-        return jaxbService.fromXml(CommandDto.class, getMemento());
-    }
+    @Property(domainEvent = CommandDtoDomainEvent.class)
+    @PropertyLayout(multiLine = 9)
+    @Getter @Setter
+    private CommandDto commandDto;
 
 
     public static class StartedAtDomainEvent extends PropertyDomainEvent<Timestamp> { }
     @javax.jdo.annotations.Persistent
     @javax.jdo.annotations.Column(allowsNull="true")
-    private Timestamp startedAt;
-    /**
-     * {@inheritDoc}
-     */
     @Property(domainEvent = StartedAtDomainEvent.class)
-    @Override
-    public Timestamp getStartedAt() {
-        return startedAt;
-    }
+    @Getter @Setter
+    private Timestamp startedAt;
 
 
     public static class CompletedAtDomainEvent extends PropertyDomainEvent<Timestamp> { }
     @javax.jdo.annotations.Persistent
     @javax.jdo.annotations.Column(allowsNull="true")
-    private Timestamp completedAt;
-    /**
-     * {@inheritDoc}
-     */
     @Property(domainEvent = CompletedAtDomainEvent.class)
-    @Override
-    public Timestamp getCompletedAt() {
-        return completedAt;
-    }
+    @Getter @Setter
+    private Timestamp completedAt;
 
 
     public static class DurationDomainEvent extends PropertyDomainEvent<BigDecimal> { }
@@ -534,6 +457,7 @@ public class CommandJdo extends DomainChangeAbstract
      * <p>
      * Populated only if it has {@link #getCompletedAt() completed}.
      */
+    @javax.jdo.annotations.NotPersistent
     @javax.validation.constraints.Digits(integer=5, fraction=3)
     @Property(domainEvent = DurationDomainEvent.class)
     public BigDecimal getDuration() {
@@ -560,8 +484,8 @@ public class CommandJdo extends DomainChangeAbstract
         }
         if(getException() != null) {
             return "EXCEPTION";
-        } 
-        if(getResultStr() != null) {
+        }
+        if(getResult() != null) {
             return "OK";
         } else {
             return "OK (VOID)";
@@ -569,45 +493,37 @@ public class CommandJdo extends DomainChangeAbstract
     }
 
 
-    @Programmatic
-    @Override
-    public Bookmark getResult() {
-        return bookmarkFor(getResultStr());
-    }
-    @Programmatic
-    public void setResult(final Bookmark result) {
-        setResultStr(asString(result));
-    }
-
-
-    public static class ResultStrDomainEvent extends PropertyDomainEvent<String> { }
+    public static class ResultDomainEvent extends PropertyDomainEvent<String> { }
+    @javax.jdo.annotations.Persistent
     @javax.jdo.annotations.Column(allowsNull="true", length = 2000, name="result")
-    @Property(domainEvent = ResultStrDomainEvent.class)
+    @Property(domainEvent = ResultDomainEvent.class)
     @PropertyLayout(hidden = Where.ALL_TABLES, named = "Result Bookmark")
     @Getter @Setter
-    private String resultStr;
+    private Bookmark result;
 
 
     public static class ExceptionDomainEvent extends PropertyDomainEvent<String> { }
     /**
      * Stack trace of any exception that might have occurred if this interaction/transaction aborted.
-     * 
+     *
      * <p>
      * Not part of the applib API, because the default implementation is not persistent
      * and so there's no object that can be accessed to be annotated.
      */
     @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB")
-    private String exception;
-    /**
-     * {@inheritDoc}
-     */
     @Property(domainEvent = ExceptionDomainEvent.class)
     @PropertyLayout(hidden = Where.ALL_TABLES, multiLine = 5, named = "Exception (if any)")
-    @Override
-    public String getException() {
-        return exception;
+    @Getter
+    private String exception;
+    public void setException(String exception) {
+        this.exception = exception;
+    }
+    public void setException(final Throwable exception) {
+        val stackTraceStr =
+                _Exceptions.streamStacktraceLines(exception, 1000)
+                .collect(Collectors.joining("\n"));
+        setException(stackTraceStr);
     }
-
 
     public static class IsCausedExceptionDomainEvent extends PropertyDomainEvent<Boolean> { }
     @javax.jdo.annotations.NotPersistent
@@ -618,204 +534,56 @@ public class CommandJdo extends DomainChangeAbstract
     }
 
 
-
-    private final LinkedList<org.apache.isis.applib.events.domain.ActionDomainEvent<?>> actionDomainEvents = new LinkedList<>();
-    @Programmatic
-    public org.apache.isis.applib.events.domain.ActionDomainEvent<?> peekActionDomainEvent() {
-        return actionDomainEvents.isEmpty()? null: actionDomainEvents.getLast();
-    }
-    @Programmatic
-    public void pushActionDomainEvent(final org.apache.isis.applib.events.domain.ActionDomainEvent<?> event) {
-        if(peekActionDomainEvent() == event) {
-            return;
-        }
-        this.actionDomainEvents.add(event);
-    }
-    @Programmatic
-    public org.apache.isis.applib.events.domain.ActionDomainEvent<?> popActionDomainEvent() {
-        return !actionDomainEvents.isEmpty()
-                ? actionDomainEvents.removeLast() : null;
-    }
-    @Programmatic
-    public List<org.apache.isis.applib.events.domain.ActionDomainEvent<?>> flushActionDomainEvents() {
-        final List<org.apache.isis.applib.events.domain.ActionDomainEvent<?>> events =
-                Collections.unmodifiableList(new ArrayList<>(actionDomainEvents));
-        actionDomainEvents.clear();
-        return events;
-    }
-
-
-    private final Map<String, AtomicInteger> sequenceByName = new HashMap<>();
-    @Programmatic
-    public int next(final String sequenceAbbr) {
-        AtomicInteger next = sequenceByName.get(sequenceAbbr);
-        if(next == null) {
-            next = new AtomicInteger(0);
-            sequenceByName.put(sequenceAbbr, next);
-        } else {
-            next.incrementAndGet();
-        }
-        return next.get();
+    @Override
+    public String toString() {
+        return ObjectContracts
+                .toString("uniqueId", CommandJdo::getUniqueId)
+                .thenToString("username", CommandJdo::getUsername)
+                .thenToString("timestamp", CommandJdo::getTimestamp)
+                .thenToString("target", CommandJdo::getTarget)
+                .thenToString("logicalMemberIdentifier", CommandJdo::getLogicalMemberIdentifier)
+                .thenToStringOmitIfAbsent("startedAt", CommandJdo::getStartedAt)
+                .thenToStringOmitIfAbsent("completedAt", CommandJdo::getCompletedAt)
+                .toString(this);
     }
 
-
-
-    @javax.jdo.annotations.NotPersistent
-    @Programmatic
-    private CommandPersistence persistence;
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public CommandPersistence getPersistence() {
-        return persistence;
+    public int compareTo(final CommandJdo other) {
+        return this.getTimestamp().compareTo(other.getTimestamp());
     }
 
 
-    @javax.jdo.annotations.NotPersistent
-    @Programmatic
-    private boolean persistHint;
     /**
-     * {@inheritDoc}
+     * @return in seconds, to 3 decimal places.
      */
-    @Override
-    public boolean isPersistHint() {
-        return persistHint;
-    }
-
-
-    boolean shouldPersist() {
-        switch (getPersistence()) {
-            case PERSISTED:
-                return true;
-            case IF_HINTED:
-                return isPersistHint();
-            default:
-                return false;
+    private static BigDecimal durationBetween(Timestamp startedAt, Timestamp completedAt) {
+        if (completedAt == null) {
+            return null;
+        } else {
+            long millis = completedAt.getTime() - startedAt.getTime();
+            return toSeconds(millis);
         }
     }
 
+    private static final BigDecimal DIVISOR = new BigDecimal(1000);
 
-    private final Command.Internal INTERNAL = new Command.Internal() {
-        @Override
-        public void setMemberIdentifier(String actionIdentifier) {
-            CommandJdo.this.memberIdentifier = actionIdentifier;
-        }
-        @Override
-        public void setTargetClass(String targetClass) {
-            CommandJdo.this.targetClass = targetClass;
-        }
-        @Override
-        public void setTargetAction(String targetAction) {
-            CommandJdo.this.targetAction = targetAction;
-        }
-        @Override
-        public void setArguments(String arguments) {
-            CommandJdo.this.arguments = arguments;
-        }
-        @Override
-        public void setMemento(String memento) {
-            CommandJdo.this.memento = memento;
-        }
-        @Override
-        public void setTarget(Bookmark target) {
-            CommandJdo.this.setTarget(target);
-        }
-        @Override
-        public void setTimestamp(Timestamp timestamp) {
-            CommandJdo.this.timestamp = timestamp;
-        }
-        @Override
-        public void setStartedAt(Timestamp startedAt) {
-            CommandJdo.this.startedAt = startedAt;
-        }
-        @Override
-        public void setCompletedAt(final Timestamp completed) {
-            CommandJdo.this.completedAt = completed;
-        }
-        @Override
-        public void setUser(String user) {
-            CommandJdo.this.username = user;
-        }
-        @Override
-        public void setParent(Command parent) {
-            CommandJdo.this.parent = parent;
-        }
-        @Override
-        public void setResult(final Bookmark result) {
-            CommandJdo.this.setResult(result);
-        }
-        @Override
-        public void setException(final String exceptionStackTrace) {
-            CommandJdo.this.exception = exceptionStackTrace;
-        }
-        @Override
-        public void setPersistence(CommandPersistence persistence) {
-            CommandJdo.this.persistence = persistence;
-        }
-        @Override
-        public void setPersistHint(boolean persistHint) {
-            CommandJdo.this.persistHint = persistHint;
-        }
-        @Override
-        public void setExecutor(Executor executor) {
-            CommandJdo.this.executor = executor;
-        }
-        @Override
-        public void setExecuteIn(CommandExecuteIn executeIn) {
-            CommandJdo.this.executeIn = executeIn;
-        }
-    };
-
-    @Override
-    public Command.Internal internal() {
-        return INTERNAL;
+    private static BigDecimal toSeconds(long millis) {
+        return new BigDecimal(millis)
+                    .divide(DIVISOR, RoundingMode.HALF_EVEN)
+                    .setScale(3, RoundingMode.HALF_EVEN);
     }
 
 
     @Override
-    public String toString() {
-        return "CommandJdo{" +
-                "targetStr='" + targetStr + '\'' +
-                ", memberIdentifier='" + memberIdentifier + '\'' +
-                ", username='" + username + '\'' +
-                ", startedAt=" + startedAt +
-                ", completedAt=" + completedAt +
-                ", transactionId=" + transactionId +
-                '}';
+    public String getPreValue() {
+        return null;
     }
 
     @Override
-    public int compareTo(final CommandJdo other) {
-        return this.getTimestamp().compareTo(other.getTimestamp());
-    }
-
-
-    private static String abbreviated(String str, int maxLength) {
-        return str != null
-                ? (str.length() < maxLength ? str : str.substring(0, maxLength - 3) + "...")
-                : null;
-    }
-
-    private static Bookmark bookmarkFor(String str) {
-        return Bookmark.parse(str).orElse(null);
-    }
-
-    private static String asString(Bookmark bookmark) {
-        return bookmark != null ? bookmark.toString() : null;
-    }
-
-    private static BigDecimal durationBetween(Timestamp startedAt, Timestamp completedAt) {
-        if (completedAt == null) {
-            return null;
-        } else {
-            long millis = completedAt.getTime() - startedAt.getTime();
-            return (new BigDecimal(millis)).divide(new BigDecimal(1000)).setScale(3, RoundingMode.HALF_EVEN);
-        }
+    public String getPostValue() {
+        return null;
     }
 
-
-    @javax.inject.Inject
-    JaxbService jaxbService;
+    @Inject JaxbService jaxbService;
 
 }
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandServiceJdoRepository.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdoRepository.java
similarity index 76%
rename from extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandServiceJdoRepository.java
rename to extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdoRepository.java
index 61eae44..37b021c 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandServiceJdoRepository.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdoRepository.java
@@ -1,3 +1,21 @@
+/*
+ *  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.extensions.commandlog.impl.jdo;
 
 import java.sql.Timestamp;
@@ -25,7 +43,6 @@ import org.apache.isis.applib.query.QueryDefault;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.CommandContext;
-import org.apache.isis.applib.services.command.CommandWithDto;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
 import org.apache.isis.persistence.jdo.applib.services.IsisJdoSupport_v3_2;
@@ -43,11 +60,11 @@ import lombok.extern.log4j.Log4j2;
  * {@link CommandJdo command} entities.
  */
 @Service
-@Named("isisExtensionsCommandLog.CommandServiceJdoRepository")
+@Named("isisExtensionsCommandLog.CommandJdoRepository")
 @Order(OrderPrecedence.MIDPOINT)
 @Qualifier("Jdo")
 @Log4j2
-public class CommandServiceJdoRepository {
+public class CommandJdoRepository {
 
     public List<CommandJdo> findByFromAndTo(
             final LocalDate from, final LocalDate to) {
@@ -80,12 +97,19 @@ public class CommandServiceJdoRepository {
     }
 
 
-    public Optional<CommandJdo> findByTransactionId(final UUID transactionId) {
+    public Optional<CommandJdo> findByUniqueId(final UUID transactionId) {
         persistCurrentCommandIfRequired();
         return repositoryService.firstMatch(
                 new QueryDefault<>(CommandJdo.class,
-                        "findByTransactionId", 
-                        "transactionId", transactionId));
+                        "findByUniqueId",
+                        "uniqueId", transactionId));
+    }
+
+    public List<CommandJdo> findByParent(final CommandJdo parent) {
+        return repositoryService.allMatches(
+                new QueryDefault<>(CommandJdo.class,
+                        "findByParent",
+                        "parent", parent));
     }
 
 
@@ -104,14 +128,11 @@ public class CommandServiceJdoRepository {
 
 
     private void persistCurrentCommandIfRequired() {
-        if(commandContextProvider == null || commandService == null) {
+        if(commandContextProvider == null) {
             return;
         } 
         final Command command = commandContextProvider.get().getCommand();
-        final CommandJdo commandJdo = commandService.asUserInitiatedCommandJdo(command);
-        if(commandJdo == null) {
-            return;
-        }
+        final CommandJdo commandJdo = new CommandJdo(command, this);
         repositoryService.persist(commandJdo);
     }
 
@@ -173,13 +194,6 @@ public class CommandServiceJdoRepository {
     }
 
 
-    public List<CommandJdo> findRecentBackgroundByTarget(Bookmark target) {
-        final String targetStr = target.toString();
-        return repositoryService.allMatches(
-                new QueryDefault<>(CommandJdo.class, "findRecentBackgroundByTarget", "targetStr", targetStr));
-    }
-
-
     /**
      * Intended to support the replay of commands on a slave instance of the application.
      *
@@ -202,20 +216,20 @@ public class CommandServiceJdoRepository {
      *
      * @return
      */
-    public List<CommandJdo> findForegroundSince(final UUID transactionId, final Integer batchSize) {
+    public List<CommandJdo> findSince(final UUID transactionId, final Integer batchSize) {
         if(transactionId == null) {
-            return findForegroundFirst();
+            return findFirst();
         }
         final CommandJdo from = findByTransactionIdElseNull(transactionId);
         if(from == null) {
             return null;
         }
-        return findForegroundSince(from.getTimestamp(), batchSize);
+        return findSince(from.getTimestamp(), batchSize);
     }
 
-    private List<CommandJdo> findForegroundFirst() {
+    private List<CommandJdo> findFirst() {
         Optional<CommandJdo> firstCommandIfAny = repositoryService.firstMatch(
-                new QueryDefault<>(CommandJdo.class, "findForegroundFirst"));
+                new QueryDefault<>(CommandJdo.class, "findFirst"));
         return firstCommandIfAny
                 .map(Collections::singletonList)
                 .orElse(Collections.emptyList());
@@ -232,10 +246,10 @@ public class CommandServiceJdoRepository {
         return q.executeUnique();
     }
 
-    private List<CommandJdo> findForegroundSince(final Timestamp timestamp, final Integer batchSize) {
+    private List<CommandJdo> findSince(final Timestamp timestamp, final Integer batchSize) {
         val q = new QueryDefault<>(
                 CommandJdo.class,
-                "findForegroundSince",
+                "findSince",
                 "timestamp", timestamp);
 
         // DN generates incorrect SQL for SQL Server if count set to 1; so we set to 2 and then trim
@@ -258,8 +272,9 @@ public class CommandServiceJdoRepository {
 
         return replayableHwm
                 .orElseGet(() -> {
-            // otherwise, the most recent completed command, run in the foreground
-            // on the slave, this corresponds to a command restored from a copy of the production database
+            // otherwise, the most recent completed command, run on the slave,
+            // this corresponds to a command restored from a copy of
+            // the production database
             Optional<CommandJdo> restoredFromDbHwm = repositoryService.firstMatch(
                     new QueryDefault<>(CommandJdo.class, "findForegroundHwm"));
 
@@ -276,14 +291,6 @@ public class CommandServiceJdoRepository {
     }
 
 
-    @Programmatic
-    public List<CommandJdo> findBackgroundCommandsByParent(final CommandJdo parent) {
-        return repositoryService.allMatches(
-                new QueryDefault<>(CommandJdo.class,
-                        "findBackgroundCommandsByParent",
-                        "parent", parent));
-    }
-
 
     public List<CommandJdo> findReplayedOnSlave() {
         return repositoryService.allMatches(
@@ -312,22 +319,16 @@ public class CommandServiceJdoRepository {
 
         final CommandJdo commandJdo = new CommandJdo();
 
-        commandJdo.setTransactionId(UUID.fromString(dto.getTransactionId()));
-        commandJdo.internal().setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(dto.getTimestamp()));
-        commandJdo.internal().setUser(dto.getUser());
-        commandJdo.internal().setExecuteIn(org.apache.isis.applib.annotation.CommandExecuteIn.REPLAYABLE);
-
-        commandJdo.setTargetClass(CommandDtoUtils.getUserData(dto, CommandWithDto.USERDATA_KEY_TARGET_CLASS));
-        commandJdo.setTargetAction(CommandDtoUtils.getUserData(dto, CommandWithDto.USERDATA_KEY_TARGET_ACTION));
-        commandJdo.internal().setArguments(CommandDtoUtils.getUserData(dto, CommandWithDto.USERDATA_KEY_ARGUMENTS));
+        commandJdo.setUniqueId(UUID.fromString(dto.getTransactionId()));
+        commandJdo.setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(dto.getTimestamp()));
+        commandJdo.setUsername(dto.getUser());
 
         commandJdo.setReplayState(ReplayState.PENDING);
-        commandJdo.internal().setPersistHint(true);
 
         final OidDto firstTarget = dto.getTargets().getOid().get(0);
-        commandJdo.setTargetStr(Bookmark.from(firstTarget).toString());
-        commandJdo.internal().setMemento(CommandDtoUtils.toXml(dto));
-        commandJdo.setMemberIdentifier(dto.getMember().getMemberIdentifier());
+        commandJdo.setTarget(Bookmark.from(firstTarget));
+        commandJdo.setCommandDto(dto);
+        commandJdo.setLogicalMemberIdentifier(dto.getMember().getLogicalMemberIdentifier());
 
         persist(commandJdo);
 
@@ -335,37 +336,11 @@ public class CommandServiceJdoRepository {
     }
 
     public void persist(final CommandJdo commandJdo) {
-        withSafeTargetStr(commandJdo);
-        withSafeResultStr(commandJdo);
         repositoryService.persist(commandJdo);
     }
 
-    public void persistIfHinted(final CommandJdo commandJdo) {
-        withSafeTargetStr(commandJdo);
-        withSafeResultStr(commandJdo);
-        if(commandJdo.shouldPersist()) {
-            repositoryService.persist(commandJdo);
-        }
-    }
-
-    private static void withSafeTargetStr(final CommandJdo commandJdo) {
-        if (tooLong(commandJdo.getTargetStr())) {
-            commandJdo.setTargetStr(null);
-        }
-    }
-    private static void withSafeResultStr(final CommandJdo commandJdo) {
-        if (tooLong(commandJdo.getResultStr())) {
-            commandJdo.setResultStr(null);
-        }
-    }
-
-    private static boolean tooLong(final String str) {
-        return str != null && str.length() > 2000;
-    }
-
 
 
-    @Inject CommandServiceJdo commandService;
     @Inject Provider<CommandContext> commandContextProvider;
     @Inject RepositoryService repositoryService;
     @Inject IsisJdoSupport_v3_2 isisJdoSupport;
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_childCommands.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_childCommands.java
index d30d566..2db6437 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_childCommands.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_childCommands.java
@@ -1,3 +1,21 @@
+/*
+ *  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.extensions.commandlog.impl.jdo;
 
 import java.util.List;
@@ -6,7 +24,6 @@ import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
-import org.apache.isis.extensions.commandlog.impl.background.BackgroundCommandServiceJdoRepository;
 
 
 @Collection(domainEvent = CommandJdo_childCommands.CollectionDomainEvent.class)
@@ -23,10 +40,10 @@ public class CommandJdo_childCommands {
 
     @MemberOrder(sequence = "100.100")
     public List<CommandJdo> coll() {
-        return backgroundCommandRepository.findByParent(commandJdo);
+        return commandJdoRepository.findByParent(commandJdo);
     }
 
     @javax.inject.Inject
-    private BackgroundCommandServiceJdoRepository backgroundCommandRepository;
+    private CommandJdoRepository commandJdoRepository;
     
 }
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_openResultObject.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_openResultObject.java
index 4399841..24689de 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_openResultObject.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_openResultObject.java
@@ -1,3 +1,21 @@
+/*
+ *  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.extensions.commandlog.impl.jdo;
 
 import org.apache.isis.applib.annotation.Action;
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_retry.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_retry.java
index 7411b97..d251482 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_retry.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_retry.java
@@ -1,3 +1,21 @@
+/*
+ *  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.extensions.commandlog.impl.jdo;
 
 import java.util.Arrays;
@@ -7,88 +25,37 @@ import java.util.List;
 import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.CommandExecuteIn;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.command.CommandContext;
+import org.apache.isis.applib.services.command.CommandExecutorService;
 import org.apache.isis.applib.services.jaxb.JaxbService;
+import org.apache.isis.applib.util.schema.CommandDtoUtils;
 import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
-import org.apache.isis.extensions.commandlog.impl.background.BackgroundCommandServiceJdo;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
+import lombok.val;
+
 @Action(
     semantics = SemanticsOf.NON_IDEMPOTENT_ARE_YOU_SURE
     , domainEvent = CommandJdo_retry.ActionDomainEvent.class
 )
 public class CommandJdo_retry {
 
-    public static enum Mode {
-        SCHEDULE_NEW,
-        REUSE
-    }
-
     private final CommandJdo commandJdo;
     public CommandJdo_retry(CommandJdo commandJdo) {
         this.commandJdo = commandJdo;
     }
 
-
     public static class ActionDomainEvent extends IsisModuleExtCommandLogImpl.ActionDomainEvent<CommandJdo_retry> { }
     @MemberOrder(name = "executeIn", sequence = "1")
-    public CommandJdo act(final Mode mode) {
-
-        switch (mode) {
-        case SCHEDULE_NEW:
-            final String memento = commandJdo.getMemento();
-            final CommandDto dto = jaxbService.fromXml(CommandDto.class, memento);
-            backgroundCommandServiceJdo.schedule(
-                    dto, commandContext.getCommand(), commandJdo.getTargetClass(), commandJdo.getTargetAction(), commandJdo.getArguments());
-            break;
-        case REUSE:
-            // will cause it to be picked up next time around
-            commandJdo.internal().setStartedAt(null);
-            commandJdo.internal().setException(null);
-            commandJdo.internal().setCompletedAt(null);
-            commandJdo.setResult(null);
-            commandJdo.setReplayState(null);
-            break;
-        default:
-            // shouldn't occur
-            throw new IllegalStateException(String.format("Probable framework error, unknown mode: %s", mode));
-        }
-        return commandJdo;
-    }
+    public CommandJdo act() {
 
-    public List<Mode> choices0Act() {
-        CommandExecuteIn executeIn = commandJdo.getExecuteIn();
-        switch (executeIn){
-            case FOREGROUND:
-            case BACKGROUND:
-                return Arrays.asList(Mode.SCHEDULE_NEW, Mode.REUSE);
-            case REPLAYABLE:
-                return Collections.singletonList(Mode.REUSE);
-            default:
-                // shouldn't occur
-                throw new IllegalStateException(String.format("Probable framework error, unknown executeIn: %s", executeIn));
-        }
-    }
+        val dto = commandJdo.getCommandDto();
+        commandExecutorService.executeCommand(dto);
 
-    public Mode default0Act() {
-        return choices0Act().get(0);
-    }
-    public String disableAct() {
-        if (!commandJdo.isComplete()) {
-            return "Not yet completed";
-        }
-        return null;
+        return commandJdo;
     }
 
-
-    @Inject
-    CommandContext commandContext;
-    @Inject
-    BackgroundCommandServiceJdo backgroundCommandServiceJdo;
-    @Inject
-    JaxbService jaxbService;
-
+    @Inject CommandExecutorService commandExecutorService;
 }
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_siblingCommands.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_siblingCommands.java
index 1f31cb6..c01a210 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_siblingCommands.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandJdo_siblingCommands.java
@@ -1,14 +1,33 @@
+/*
+ *  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.extensions.commandlog.impl.jdo;
 
 import java.util.Collections;
 import java.util.List;
 
+import javax.inject.Inject;
+
 import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
-import org.apache.isis.extensions.commandlog.impl.background.BackgroundCommandServiceJdoRepository;
 
 @Collection(domainEvent = CommandJdo_siblingCommands.CollectionDomainEvent.class)
 @CollectionLayout(defaultView = "table")
@@ -24,18 +43,16 @@ public class CommandJdo_siblingCommands {
 
     @MemberOrder(sequence = "100.110")
     public List<CommandJdo> coll() {
-        final Command parent = commandJdo.getParent();
-        if(!(parent instanceof CommandJdo)) {
+        final CommandJdo parentJdo = commandJdo.getParent();
+        if(parentJdo == null) {
             return Collections.emptyList();
         }
-        final CommandJdo parentJdo = (CommandJdo) parent;
-        final List<CommandJdo> siblingCommands = backgroundCommandRepository.findByParent(parentJdo);
+        final List<CommandJdo> siblingCommands = commandJdoRepository.findByParent(parentJdo);
         siblingCommands.remove(commandJdo);
         return siblingCommands;
     }
 
 
-    @javax.inject.Inject
-    private BackgroundCommandServiceJdoRepository backgroundCommandRepository;
+    @Inject CommandJdoRepository commandJdoRepository;
     
 }
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandServiceJdo.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandServiceJdo.java
deleted file mode 100644
index 75e2dd2..0000000
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/CommandServiceJdo.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package org.apache.isis.extensions.commandlog.impl.jdo;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-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.CommandExecuteIn;
-import org.apache.isis.applib.annotation.CommandPersistence;
-import org.apache.isis.applib.annotation.DomainService;
-import org.apache.isis.applib.annotation.OrderPrecedence;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.Command.Executor;
-import org.apache.isis.applib.services.command.spi.CommandService;
-import org.apache.isis.applib.services.factory.FactoryService;
-import org.apache.isis.applib.services.repository.RepositoryService;
-
-import lombok.extern.log4j.Log4j2;
-
-@Service
-@Named("isisExtensionsCommandLog.CommandServiceJdo")
-@Order(OrderPrecedence.MIDPOINT)
-@Qualifier("Jdo")
-@Log4j2
-public class CommandServiceJdo implements CommandService {
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public Command create() {
-        CommandJdo command = factoryService.instantiate(CommandJdo.class);
-        command.internal().setExecutor(Executor.OTHER);
-        command.internal().setPersistence(CommandPersistence.IF_HINTED);
-        return command;
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void complete(final Command command) {
-        final CommandJdo commandJdo = asUserInitiatedCommandJdo(command);
-        if(commandJdo == null) {
-            return;
-        }
-        commandServiceJdoRepository.persistIfHinted(commandJdo);
-
-    }
-
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean persistIfPossible(Command command) {
-        if(!(command instanceof CommandJdo)) {
-            // ought not to be the case, since this service created the object in the #create() method
-            return false;
-        }
-        final CommandJdo commandJdo = (CommandJdo)command;
-        repositoryService.persist(commandJdo);
-        return true;
-    }
-
-
-    /**
-     * Not API, also used by {@link CommandServiceJdoRepository}.
-     */
-    CommandJdo asUserInitiatedCommandJdo(final Command command) {
-        if(!(command instanceof CommandJdo)) {
-            // ought not to be the case, since this service created the object in the #create() method
-            return null;
-        }
-        if(command.getExecuteIn() != CommandExecuteIn.FOREGROUND) {
-            return null;
-        } 
-        final CommandJdo commandJdo = (CommandJdo) command;
-        return commandJdo.shouldPersist()? commandJdo: null;
-    }
-
-
-
-    @Inject
-    RepositoryService repositoryService;
-
-    @Inject
-    CommandServiceJdoRepository commandServiceJdoRepository;
-
-    @Inject
-    FactoryService factoryService;
-
-}
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/ReplayState.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/ReplayState.java
index 93dd52f..e4ebdb6 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/ReplayState.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/jdo/ReplayState.java
@@ -1,9 +1,43 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
 package org.apache.isis.extensions.commandlog.impl.jdo;
 
 public enum ReplayState {
+    /**
+     * As used on primary system.
+     */
+    UNDEFINED,
+    /**
+     * For use on secondary system, indicates that the command has not yet been replayed.
+     */
     PENDING,
+    /**
+     * For use on secondary system, indicates that the command has been replayed ok
+     */
     OK,
+    /**
+     * For use on secondary system, indicates that the command has been replayed but encountered an error
+     */
     FAILED,
+    /**
+     * For use on secondary system, indicates that the command should not be replayed.
+     */
     EXCLUDED,
     ;
 
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/HasUniqueId_command.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/HasUniqueId_command.java
index 8deda59..3766317 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/HasUniqueId_command.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/HasUniqueId_command.java
@@ -12,7 +12,7 @@ import org.apache.isis.applib.services.command.Command;
 
 import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 
 
 /**
@@ -53,9 +53,10 @@ public class HasUniqueId_command {
     private CommandJdo findCommand() {
         final UUID transactionId = hasUniqueId.getUniqueId();
         return commandServiceRepository
-                .findByTransactionId(transactionId)
+                .findByUniqueId(transactionId)
                 .orElse(null);
     }
 
-    @Inject CommandServiceJdoRepository commandServiceRepository;
+    @Inject
+    CommandJdoRepository commandServiceRepository;
 }
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/HasUsername_recentCommandsByUser.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/HasUsername_recentCommandsByUser.java
index adacb08..a94f9a8 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/HasUsername_recentCommandsByUser.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/HasUsername_recentCommandsByUser.java
@@ -11,7 +11,7 @@ import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.services.HasUsername;
 import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 
 
 @Collection(
@@ -41,5 +41,6 @@ public class HasUsername_recentCommandsByUser {
         return hasUsername.getUsername() == null;
     }
 
-    @Inject CommandServiceJdoRepository commandServiceRepository;
+    @Inject
+    CommandJdoRepository commandServiceRepository;
 }
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/Object_recentCommands.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/Object_recentCommands.java
index 2be947a..bd8af7d 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/Object_recentCommands.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/Object_recentCommands.java
@@ -14,7 +14,7 @@ import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 
 /**
  * This mixin contributes a <tt>recentCommands</tt> action to any domain object
@@ -53,7 +53,7 @@ public class Object_recentCommands {
     }
 
     @Inject
-    CommandServiceJdoRepository commandServiceRepository;
+    CommandJdoRepository commandServiceRepository;
     @Inject BookmarkService bookmarkService;
 
 }
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/T_backgroundCommands.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/T_backgroundCommands.java
index e997ab1..8e48b3e 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/T_backgroundCommands.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/mixins/T_backgroundCommands.java
@@ -11,7 +11,7 @@ import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
 import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 
 @Collection(
     domainEvent = T_backgroundCommands.CollectionDomainEvent.class
@@ -35,13 +35,14 @@ public abstract class T_backgroundCommands<T> {
     private List<CommandJdo> findRecentBackground() {
         final Bookmark bookmark = bookmarkService.bookmarkFor(domainObject);
         return queryResultsCache.execute(
-                () -> commandServiceJdoRepository.findRecentBackgroundByTarget(bookmark)
+                () -> commandJdoRepository.findRecentBackgroundByTarget(bookmark)
                 , T_backgroundCommands.class
                 , "findRecentBackground"
                 , domainObject);
     }
 
-    @Inject CommandServiceJdoRepository commandServiceJdoRepository;
+    @Inject
+    CommandJdoRepository commandJdoRepository;
     @Inject BookmarkService bookmarkService;
     @Inject QueryResultsCache queryResultsCache;
 
diff --git a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/ui/CommandServiceMenu.java b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/ui/CommandServiceMenu.java
index f03d94b..685e784 100644
--- a/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/ui/CommandServiceMenu.java
+++ b/extensions/core/command-log/impl/src/main/java/org/apache/isis/extensions/commandlog/impl/ui/CommandServiceMenu.java
@@ -26,7 +26,7 @@ import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.clock.ClockService;
 import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 
 @DomainService(
     nature = NatureOfService.VIEW,
@@ -94,14 +94,15 @@ public class CommandServiceMenu {
     public CommandJdo findCommandById(
             @ParameterLayout(named="Transaction Id")
             final UUID transactionId) {
-        return commandServiceRepository.findByTransactionId(transactionId).orElse(null);
+        return commandServiceRepository.findByUniqueId(transactionId).orElse(null);
     }
     public boolean hideFindCommandById() {
         return commandServiceRepository == null;
     }
 
 
-    @Inject CommandServiceJdoRepository commandServiceRepository;
+    @Inject
+    CommandJdoRepository commandServiceRepository;
     @Inject ClockService clockService;
 }
 
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserExceptionStr.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserExceptionStr.java
index e1571a0..1d98c15 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserExceptionStr.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserExceptionStr.java
@@ -4,7 +4,6 @@ import com.google.common.base.Objects;
 
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandWithDto;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserNumberBackgroundCommands.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserNumberBackgroundCommands.java
index aaedd95..565d784 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserNumberBackgroundCommands.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserNumberBackgroundCommands.java
@@ -11,7 +11,7 @@ import org.apache.isis.applib.util.schema.CommandDtoUtils;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 
 @DomainService()
 public class CommandReplayAnalyserNumberBackgroundCommands
@@ -37,7 +37,7 @@ public class CommandReplayAnalyserNumberBackgroundCommands
             final CommandJdo commandJdo = (CommandJdo) command;
 
             final List<CommandJdo> backgroundCommands =
-                    commandServiceJdoRepository.findBackgroundCommandsByParent(commandJdo);
+                    commandJdoRepository.findBackgroundCommandsByParent(commandJdo);
 
             CommandDtoUtils.setUserData(commandDto,
                     USERDATA_KEY_NUMBER_BACKGROUND_COMMANDS, ""+backgroundCommands.size());
@@ -73,7 +73,7 @@ public class CommandReplayAnalyserNumberBackgroundCommands
 
         final CommandJdo commandJdo = (CommandJdo) command;
         final List<CommandJdo> backgroundCommands =
-                commandServiceJdoRepository.findBackgroundCommandsByParent(commandJdo);
+                commandJdoRepository.findBackgroundCommandsByParent(commandJdo);
 
         final int slaveNumBackgroundCommands = backgroundCommands.size();
         if (masterNumBackgroundCommands == slaveNumBackgroundCommands) {
@@ -84,6 +84,7 @@ public class CommandReplayAnalyserNumberBackgroundCommands
                 masterNumBackgroundCommands, slaveNumBackgroundCommands);
     }
 
-    @Inject CommandServiceJdoRepository commandServiceJdoRepository;
+    @Inject
+    CommandJdoRepository commandJdoRepository;
 
 }
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserResultStr.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserResultStr.java
index 1c4a01c..7aa5177 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserResultStr.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalyserResultStr.java
@@ -4,7 +4,6 @@ import com.google.common.base.Objects;
 
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandWithDto;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalysisService.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalysisService.java
index b5317c0..ab1936e 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalysisService.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/analysis/CommandReplayAnalysisService.java
@@ -8,7 +8,6 @@ import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.Programmatic;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
 import org.apache.isis.extensions.commandlog.impl.jdo.ReplayState;
-import org.apache.isis.extensions.commandreplay.impl.analysis.CommandReplayAnalyser;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
 import lombok.extern.log4j.Log4j2;
@@ -34,7 +33,7 @@ public class CommandReplayAnalysisService {
     }
 
     private String analyseReplay(final CommandJdo commandJdo) {
-        final CommandDto dto = commandJdo.asDto();
+        final CommandDto dto = commandJdo.getCommandDto();
 
         for (final CommandReplayAnalyser analyser : analysers) {
             try {
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/executor/CommandExecutorServiceWithTime.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/executor/CommandExecutorServiceWithTime.java
index 0c2e724..59abc45 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/executor/CommandExecutorServiceWithTime.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/executor/CommandExecutorServiceWithTime.java
@@ -1,4 +1,4 @@
-/**
+/*
  *  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.
@@ -26,7 +26,6 @@ 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.CommandExecutorService;
-import org.apache.isis.applib.services.command.CommandWithDto;
 import org.apache.isis.extensions.commandreplay.impl.clock.TickingClockService;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
@@ -57,10 +56,10 @@ public class CommandExecutorServiceWithTime implements CommandExecutorService {
     }
 
     @Override
-    public void executeCommand(final SudoPolicy sudoPolicy, final CommandWithDto commandWithDto) {
-        final Runnable executeCommand = () -> delegate.executeCommand(sudoPolicy, commandWithDto);
+    public void executeCommand(final SudoPolicy sudoPolicy, final Command command) {
+        final Runnable executeCommand = () -> delegate.executeCommand(sudoPolicy, command);
         if(tickingClockService.isInitialized()) {
-            final Timestamp timestamp = commandWithDto.getTimestamp();
+            final Timestamp timestamp = command.getTimestamp();
             tickingClockService.at(timestamp, executeCommand);
         } else {
             executeCommand.run();
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/executor/ReplayableCommandExecution.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/executor/ReplayableCommandExecution.java
index 700a639..5da0059 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/executor/ReplayableCommandExecution.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/executor/ReplayableCommandExecution.java
@@ -18,7 +18,7 @@ import org.apache.isis.extensions.commandreplay.impl.util.Holder;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 
 import lombok.extern.log4j.Log4j2;
 
@@ -57,7 +57,7 @@ public class ReplayableCommandExecution
                 // first time through the loop we need to find the HWM command
                 // (subsequent iterations we use the command from before as the HWM)
                 log.debug("searching for hwm on slave ...");
-                hwmCommand = commandServiceJdoRepository.findReplayHwm();
+                hwmCommand = commandJdoRepository.findReplayHwm();
             }
 
             if(hwmCommand == null) {
@@ -67,7 +67,7 @@ public class ReplayableCommandExecution
             }
 
             log.debug("current hwm transactionId = {} {} {} {}",
-                    hwmCommand.getTransactionId(), hwmCommand.getTimestamp(),
+                    hwmCommand.getUniqueId(), hwmCommand.getTimestamp(),
                     hwmCommand.getExecuteIn(), hwmCommand.getMemberIdentifier());
 
 
@@ -89,7 +89,7 @@ public class ReplayableCommandExecution
                     // check that the current HWM was replayed successfully, otherwise break out
                     //
                     if(hwmCommand.getReplayState().isFailed()) {
-                        log.info("Command xactnId={} hit replay error", hwmCommand.getTransactionId());
+                        log.info("Command xactnId={} hit replay error", hwmCommand.getUniqueId());
                         return;
                     }
                     fetchNext = true;
@@ -99,7 +99,7 @@ public class ReplayableCommandExecution
             default:
                 log.error(
                         "HWM command xactnId={} should be either FOREGROUND or REPLAYABLE but is instead {}; aborting",
-                        hwmCommand.getTransactionId(), hwmCommand.getExecuteIn());
+                        hwmCommand.getUniqueId(), hwmCommand.getExecuteIn());
                 return;
             }
 
@@ -114,10 +114,10 @@ public class ReplayableCommandExecution
                 }
 
                 hwmCommand = transactionService.executeWithinTransaction(
-                        () -> commandServiceJdoRepository.saveForReplay(commandDto));
+                        () -> commandJdoRepository.saveForReplay(commandDto));
             }
 
-            log.info("next HWM transactionId = {} {} {} {}", hwmCommand.getTransactionId());
+            log.info("next HWM transactionId = {} {} {} {}", hwmCommand.getUniqueId());
 
 
 
@@ -133,7 +133,7 @@ public class ReplayableCommandExecution
             final CommandJdo parent = hwmCommand;
             final List<CommandJdo> backgroundCommands =
                     transactionService.executeWithinTransaction(
-                            () -> commandServiceJdoRepository.findBackgroundCommandsByParent(parent));
+                            () -> commandJdoRepository.findBackgroundCommandsByParent(parent));
             for (final CommandJdo backgroundCommand : backgroundCommands) {
                 execute(backgroundCommand, transactionService);
             }
@@ -169,7 +169,8 @@ public class ReplayableCommandExecution
     @Inject TransactionService transactionService;
     @Inject
     CommandFetcher commandFetcher;
-    @Inject CommandServiceJdoRepository commandServiceJdoRepository;
+    @Inject
+    CommandJdoRepository commandJdoRepository;
     @Inject
     CommandReplayAnalysisService analysisService;
     @Inject ReplayCommandExecutionController controller;
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_download.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_download.java
index 6c25cd1..6faef66 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_download.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_download.java
@@ -4,7 +4,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.MemberOrder;
 import org.apache.isis.applib.annotation.ParameterLayout;
 import org.apache.isis.applib.annotation.SemanticsOf;
@@ -38,7 +37,7 @@ public class CommandJdo_download {
     public Clob act(
             @ParameterLayout(named="Filename prefix")
             final String fileNamePrefix) {
-        return commandReplayOnMasterService.downloadCommandById(commandJdo.getTransactionId(), fileNamePrefix);
+        return commandReplayOnMasterService.downloadCommandById(commandJdo.getUniqueId(), fileNamePrefix);
     }
     public String default0Act() {
         return "command";
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_exclude.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_exclude.java
index 0c89af5..071f818 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_exclude.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_exclude.java
@@ -1,7 +1,6 @@
 package org.apache.isis.extensions.commandreplay.impl.mixins;
 
 import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.extensions.commandlog.impl.IsisModuleExtCommandLogImpl;
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_replayNext.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_replayNext.java
index e09d0b9..93f9a7a 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_replayNext.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_replayNext.java
@@ -5,13 +5,12 @@ import java.util.List;
 import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.CommandPersistence;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.command.CommandExecutorService;
 import org.apache.isis.applib.services.message.MessageService;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 import org.apache.isis.extensions.commandreplay.impl.IsisModuleExtCommandReplayImpl;
 import org.apache.isis.extensions.commandreplay.impl.fetch.MasterConfiguration;
 import org.apache.isis.extensions.commandreplay.impl.StatusException;
@@ -38,7 +37,7 @@ public class CommandJdo_replayNext {
     public CommandJdo act() throws StatusException {
 
         // double check this is still the HWM
-        final CommandJdo replayHwm = commandServiceJdoRepository.findReplayHwm();
+        final CommandJdo replayHwm = commandJdoRepository.findReplayHwm();
         if(commandJdo != replayHwm) {
             messageService.informUser("HWM has changed");
             return replayHwm;
@@ -61,7 +60,7 @@ public class CommandJdo_replayNext {
         final CommandDto commandDto = commandFetcher.fetchCommand(this.commandJdo);
         return commandDto == null
                 ? null
-                : commandServiceJdoRepository.saveForReplay(commandDto);
+                : commandJdoRepository.saveForReplay(commandDto);
     }
 
     private void execute(final CommandJdo hwmCommand) {
@@ -70,14 +69,14 @@ public class CommandJdo_replayNext {
         commandExecutorService.executeCommand(CommandExecutorService.SudoPolicy.SWITCH, hwmCommand);
 
         // find background commands, and run them
-        final List<CommandJdo> backgroundCommands = commandServiceJdoRepository.findBackgroundCommandsByParent(hwmCommand);
+        final List<CommandJdo> backgroundCommands = commandJdoRepository.findBackgroundCommandsByParent(hwmCommand);
         for (final CommandJdo backgroundCommand : backgroundCommands) {
             commandExecutorService.executeCommand(CommandExecutorService.SudoPolicy.SWITCH, backgroundCommand);
         }
     }
 
     public String disableAct() {
-        final CommandJdo replayHwm = commandServiceJdoRepository.findReplayHwm();
+        final CommandJdo replayHwm = commandJdoRepository.findReplayHwm();
 
         if(commandJdo != replayHwm) {
             return "This action can only be performed against the 'HWM' command on the slave";
@@ -103,7 +102,7 @@ public class CommandJdo_replayNext {
 
 
     @Inject
-    CommandServiceJdoRepository commandServiceJdoRepository;
+    CommandJdoRepository commandJdoRepository;
     @Inject CommandFetcher commandFetcher;
     @Inject CommandExecutorService commandExecutorService;
     @Inject
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_replayQueue.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_replayQueue.java
index 2f4286d..924c861 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_replayQueue.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/mixins/CommandJdo_replayQueue.java
@@ -9,7 +9,7 @@ import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.applib.annotation.MemberOrder;
 import org.apache.isis.applib.annotation.Mixin;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 import org.apache.isis.extensions.commandreplay.impl.IsisModuleExtCommandReplayImpl;
 import org.apache.isis.extensions.commandreplay.impl.fetch.MasterConfiguration;
 
@@ -32,7 +32,7 @@ public class CommandJdo_replayQueue {
 
     @MemberOrder(sequence = "100.100")
     public List<CommandJdo> coll() {
-        return commandServiceJdoRepository.findReplayedOnSlave();
+        return commandJdoRepository.findReplayedOnSlave();
     }
 
     public boolean hideColl() {
@@ -42,6 +42,6 @@ public class CommandJdo_replayQueue {
     @Inject
     MasterConfiguration masterConfiguration;
     @Inject
-    CommandServiceJdoRepository commandServiceJdoRepository;
+    CommandJdoRepository commandJdoRepository;
 
 }
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/quartz/RunBackgroundCommandsWithReplicationAndReplayJob.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/quartz/RunBackgroundCommandsWithReplicationAndReplayJob.java
index a68911f..034a3db 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/quartz/RunBackgroundCommandsWithReplicationAndReplayJob.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/quartz/RunBackgroundCommandsWithReplicationAndReplayJob.java
@@ -16,7 +16,6 @@ import org.apache.isis.core.runtime.iactn.template.AbstractIsisInteractionTempla
 import org.apache.isis.core.security.authentication.AuthenticationSession;
 import org.apache.isis.core.security.authentication.standard.SimpleSession;
 
-import org.apache.isis.extensions.commandlog.impl.background.BackgroundCommandExecutionFromBackgroundCommandServiceJdo;
 import org.apache.isis.extensions.commandreplay.impl.clock.TickingClockService;
 import org.apache.isis.extensions.commandreplay.impl.util.Holder;
 import org.apache.isis.extensions.commandreplay.impl.executor.ReplayableCommandExecution;
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/ui/CommandReplayOnMasterService.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/ui/CommandReplayOnMasterService.java
index ea55bc2..c3bb759 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/ui/CommandReplayOnMasterService.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/ui/CommandReplayOnMasterService.java
@@ -21,7 +21,7 @@ import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.services.message.MessageService;
 import org.apache.isis.applib.value.Clob;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 import org.apache.isis.extensions.commandreplay.impl.IsisModuleExtCommandReplayImpl;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.CommandsDto;
@@ -158,7 +158,7 @@ public class CommandReplayOnMasterService {
             @ParameterLayout(named="Filename prefix")
             final String fileNamePrefix) {
 
-        return commandServiceRepository.findByTransactionId(transactionId)
+        return commandServiceRepository.findByUniqueId(transactionId)
                 .map(commandJdo -> {
 
                     final CommandDto commandDto =
@@ -180,7 +180,8 @@ public class CommandReplayOnMasterService {
     }
 
 
-    @Inject CommandServiceJdoRepository commandServiceRepository;
+    @Inject
+    CommandJdoRepository commandServiceRepository;
     @Inject JaxbService jaxbService;
     @Inject MessageService messageService;
     @Inject ContentMappingServiceForCommandsDto contentMappingServiceForCommandsDto;
diff --git a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/ui/CommandReplayOnSlaveService.java b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/ui/CommandReplayOnSlaveService.java
index e30e323..cbd037c 100644
--- a/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/ui/CommandReplayOnSlaveService.java
+++ b/extensions/core/command-replay/impl/src/main/java/org/apache/isis/extensions/commandreplay/impl/ui/CommandReplayOnSlaveService.java
@@ -7,7 +7,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.CommandReification;
 import org.apache.isis.applib.annotation.DomainService;
 import org.apache.isis.applib.annotation.DomainServiceLayout;
 import org.apache.isis.applib.annotation.MemberOrder;
@@ -16,7 +15,7 @@ import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.value.Clob;
 import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdo;
-import org.apache.isis.extensions.commandlog.impl.jdo.CommandServiceJdoRepository;
+import org.apache.isis.extensions.commandlog.impl.jdo.CommandJdoRepository;
 import org.apache.isis.extensions.commandreplay.impl.IsisModuleExtCommandReplayImpl;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.CommandsDto;
@@ -43,7 +42,7 @@ public class CommandReplayOnSlaveService {
     @ActionLayout(cssClassFa = "fa-bath")
     @MemberOrder(sequence="60.1")
     public CommandJdo findReplayHwmOnSlave() {
-        return commandServiceJdoRepository.findReplayHwm();
+        return commandJdoRepository.findReplayHwm();
     }
 
 
@@ -70,14 +69,14 @@ public class CommandReplayOnSlaveService {
         }
 
         for (final CommandDto commandDto : commandDtoList) {
-            commandServiceJdoRepository.saveForReplay(commandDto);
+            commandJdoRepository.saveForReplay(commandDto);
         }
     }
 
 
 
     @Inject
-    CommandServiceJdoRepository commandServiceJdoRepository;
+    CommandJdoRepository commandJdoRepository;
     @Inject JaxbService jaxbService;
 
 }
diff --git a/extensions/core/quartz/impl/src/main/java/org/apache/isis/extensions/quartz/jobs/RunBackgroundCommandsJob.java b/extensions/core/quartz/impl/src/main/java/org/apache/isis/extensions/quartz/jobs/RunBackgroundCommandsJob.java
index c0391b8..75712e4 100644
--- a/extensions/core/quartz/impl/src/main/java/org/apache/isis/extensions/quartz/jobs/RunBackgroundCommandsJob.java
+++ b/extensions/core/quartz/impl/src/main/java/org/apache/isis/extensions/quartz/jobs/RunBackgroundCommandsJob.java
@@ -3,8 +3,6 @@ package org.apache.isis.extensions.quartz.jobs;
 
 import javax.inject.Inject;
 
-import com.google.common.base.Splitter;
-
 import org.quartz.Job;
 import org.quartz.JobExecutionContext;
 
@@ -12,8 +10,6 @@ import org.apache.isis.core.config.IsisConfiguration;
 import org.apache.isis.core.security.authentication.AuthenticationSession;
 import org.apache.isis.core.security.authentication.standard.SimpleSession;
 
-import org.apache.isis.extensions.commandlog.impl.background.BackgroundCommandExecutionFromBackgroundCommandServiceJdo;
-
 import lombok.val;
 import lombok.extern.log4j.Log4j2;
 
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisBookmarkConverter.java
similarity index 70%
copy from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
copy to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisBookmarkConverter.java
index 3daffaa..838a998 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisBookmarkConverter.java
@@ -16,27 +16,29 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.applib;
 
 import org.datanucleus.store.types.converters.TypeConverter;
 
-import org.apache.isis.applib.value.Markup;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.util.schema.ChangesDtoUtils;
+import org.apache.isis.schema.chg.v2.ChangesDto;
 
-public class IsisMarkupConverter implements TypeConverter<Markup, String>{
+public class IsisBookmarkConverter implements TypeConverter<Bookmark, String>{
 
     private static final long serialVersionUID = 1L;
 
     @Override
-    public String toDatastoreType(final Markup memberValue) {
+    public String toDatastoreType(final Bookmark memberValue) {
         return memberValue != null
-                ? memberValue.asHtml()
+                ? memberValue.toString()
                         : null;
     }
 
     @Override
-    public Markup toMemberType(final String datastoreValue) {
+    public Bookmark toMemberType(final String datastoreValue) {
         return datastoreValue != null
-                ? new Markup(datastoreValue)
+                ? Bookmark.parse(datastoreValue)
                         : null;
     }
 
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisLocalResourcePathConverter.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisLocalResourcePathConverter.java
similarity index 98%
rename from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisLocalResourcePathConverter.java
rename to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisLocalResourcePathConverter.java
index 8663815..57d593a 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisLocalResourcePathConverter.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisLocalResourcePathConverter.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.applib;
 
 import org.datanucleus.store.types.converters.TypeConverter;
 
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisMarkupConverter.java
similarity index 98%
copy from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
copy to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisMarkupConverter.java
index 3daffaa..ba910ca 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisMarkupConverter.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.applib;
 
 import org.datanucleus.store.types.converters.TypeConverter;
 
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisPasswordConverter.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisPasswordConverter.java
similarity index 98%
copy from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisPasswordConverter.java
copy to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisPasswordConverter.java
index 6fd4969..ee7dc8c 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisPasswordConverter.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/applib/IsisPasswordConverter.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.applib;
 
 import org.datanucleus.store.types.converters.TypeConverter;
 
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisChangesDtoConverter.java
similarity index 72%
copy from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
copy to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisChangesDtoConverter.java
index 3daffaa..a60e018 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisChangesDtoConverter.java
@@ -16,27 +16,28 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.schema.v2;
 
 import org.datanucleus.store.types.converters.TypeConverter;
 
-import org.apache.isis.applib.value.Markup;
+import org.apache.isis.applib.util.schema.ChangesDtoUtils;
+import org.apache.isis.schema.chg.v2.ChangesDto;
 
-public class IsisMarkupConverter implements TypeConverter<Markup, String>{
+public class IsisChangesDtoConverter implements TypeConverter<ChangesDto, String>{
 
     private static final long serialVersionUID = 1L;
 
     @Override
-    public String toDatastoreType(final Markup memberValue) {
+    public String toDatastoreType(final ChangesDto memberValue) {
         return memberValue != null
-                ? memberValue.asHtml()
+                ? ChangesDtoUtils.toXml(memberValue)
                         : null;
     }
 
     @Override
-    public Markup toMemberType(final String datastoreValue) {
+    public ChangesDto toMemberType(final String datastoreValue) {
         return datastoreValue != null
-                ? new Markup(datastoreValue)
+                ? ChangesDtoUtils.fromXml(datastoreValue)
                         : null;
     }
 
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisPasswordConverter.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisCommandDtoConverter.java
similarity index 67%
rename from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisPasswordConverter.java
rename to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisCommandDtoConverter.java
index 6fd4969..d0d304f 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisPasswordConverter.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisCommandDtoConverter.java
@@ -16,27 +16,30 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.schema.v2;
 
 import org.datanucleus.store.types.converters.TypeConverter;
 
-import org.apache.isis.applib.value.Password;
+import org.apache.isis.applib.util.schema.CommandDtoUtils;
+import org.apache.isis.applib.util.schema.InteractionDtoUtils;
+import org.apache.isis.schema.cmd.v2.CommandDto;
+import org.apache.isis.schema.ixn.v2.InteractionDto;
 
-public class IsisPasswordConverter implements TypeConverter<Password, String>{
+public class IsisCommandDtoConverter implements TypeConverter<CommandDto, String>{
 
     private static final long serialVersionUID = 1L;
 
     @Override
-    public String toDatastoreType(final Password memberValue) {
+    public String toDatastoreType(final CommandDto memberValue) {
         return memberValue != null
-                ? memberValue.getPassword()
+                ? CommandDtoUtils.toXml(memberValue)
                         : null;
     }
 
     @Override
-    public Password toMemberType(final String datastoreValue) {
+    public CommandDto toMemberType(final String datastoreValue) {
         return datastoreValue != null
-                ? new Password(datastoreValue)
+                ? CommandDtoUtils.fromXml(datastoreValue)
                         : null;
     }
 
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisInteractionDtoConverter.java
similarity index 71%
copy from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
copy to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisInteractionDtoConverter.java
index 3daffaa..1071ed0 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisInteractionDtoConverter.java
@@ -16,27 +16,29 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.schema.v2;
 
 import org.datanucleus.store.types.converters.TypeConverter;
 
+import org.apache.isis.applib.util.schema.InteractionDtoUtils;
 import org.apache.isis.applib.value.Markup;
+import org.apache.isis.schema.ixn.v2.InteractionDto;
 
-public class IsisMarkupConverter implements TypeConverter<Markup, String>{
+public class IsisInteractionDtoConverter implements TypeConverter<InteractionDto, String>{
 
     private static final long serialVersionUID = 1L;
 
     @Override
-    public String toDatastoreType(final Markup memberValue) {
+    public String toDatastoreType(final InteractionDto memberValue) {
         return memberValue != null
-                ? memberValue.asHtml()
+                ? InteractionDtoUtils.toXml(memberValue)
                         : null;
     }
 
     @Override
-    public Markup toMemberType(final String datastoreValue) {
+    public InteractionDto toMemberType(final String datastoreValue) {
         return datastoreValue != null
-                ? new Markup(datastoreValue)
+                ? InteractionDtoUtils.fromXml(datastoreValue)
                         : null;
     }
 
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisOidDtoConverter.java
similarity index 64%
copy from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
copy to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisOidDtoConverter.java
index 3daffaa..ad3980c 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/schema/v2/IsisOidDtoConverter.java
@@ -16,28 +16,33 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.schema.v2;
 
 import org.datanucleus.store.types.converters.TypeConverter;
 
-import org.apache.isis.applib.value.Markup;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.util.schema.ChangesDtoUtils;
+import org.apache.isis.schema.chg.v2.ChangesDto;
+import org.apache.isis.schema.common.v2.OidDto;
 
-public class IsisMarkupConverter implements TypeConverter<Markup, String>{
+public class IsisOidDtoConverter implements TypeConverter<OidDto, String>{
 
     private static final long serialVersionUID = 1L;
 
     @Override
-    public String toDatastoreType(final Markup memberValue) {
+    public String toDatastoreType(final OidDto memberValue) {
         return memberValue != null
-                ? memberValue.asHtml()
+                ? Bookmark.from(memberValue).toString()
                         : null;
     }
 
     @Override
-    public Markup toMemberType(final String datastoreValue) {
+    public OidDto toMemberType(final String datastoreValue) {
         return datastoreValue != null
-                ? new Markup(datastoreValue)
-                        : null;
+                ? Bookmark.parse(datastoreValue)
+                    .map(Bookmark::toOidDto)
+                    .orElse(null)
+                : null;
     }
 
 }
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuemappings/ByteArrayBlobRdbmsMapping.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuemappings/applib/ByteArrayBlobRdbmsMapping.java
similarity index 98%
rename from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuemappings/ByteArrayBlobRdbmsMapping.java
rename to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuemappings/applib/ByteArrayBlobRdbmsMapping.java
index 993e782..5165abb 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuemappings/ByteArrayBlobRdbmsMapping.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuemappings/applib/ByteArrayBlobRdbmsMapping.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuemappings;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuemappings.applib;
 
 import org.datanucleus.store.rdbms.RDBMSStoreManager;
 import org.datanucleus.store.rdbms.mapping.column.BlobColumnMapping;
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/IsisBlobMapping.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/applib/IsisBlobMapping.java
similarity index 99%
rename from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/IsisBlobMapping.java
rename to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/applib/IsisBlobMapping.java
index f3fbda2..55a3d5e 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/IsisBlobMapping.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/applib/IsisBlobMapping.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuetypes;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuetypes.applib;
 
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/IsisClobMapping.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/applib/IsisClobMapping.java
similarity index 99%
rename from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/IsisClobMapping.java
rename to persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/applib/IsisClobMapping.java
index c5f5a4b..c8e6be6 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/IsisClobMapping.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/valuetypes/applib/IsisClobMapping.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuetypes;
+package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuetypes.applib;
 
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/persistence/PersistenceSession5.java b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/persistence/PersistenceSession5.java
index 1b307c3..10c8dca 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/persistence/PersistenceSession5.java
+++ b/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/persistence/PersistenceSession5.java
@@ -75,7 +75,6 @@ import org.apache.isis.core.metamodel.facets.object.callbacks.UpdatingCallbackFa
 import org.apache.isis.core.metamodel.facets.object.callbacks.UpdatingLifecycleEventFacet;
 import org.apache.isis.core.metamodel.services.container.query.QueryCardinality;
 import org.apache.isis.core.metamodel.spec.ManagedObject;
-import org.apache.isis.core.metamodel.spec.ManagedObjects.EntityUtil;
 import org.apache.isis.core.metamodel.spec.ObjectSpecification;
 import org.apache.isis.persistence.jdo.applib.exceptions.NotPersistableException;
 import org.apache.isis.persistence.jdo.applib.exceptions.UnsupportedFindException;
@@ -176,7 +175,7 @@ implements IsisLifecycleListener.PersistenceSessionLifecycleManagement {
         final String userName = userService.getUser().getName();
 
         command.internal().setTimestamp(timestamp);
-        command.internal().setUser(userName);
+        command.internal().setUsername(userName);
 
         interaction.setUniqueId(command.getUniqueId());
 
diff --git a/persistence/jdo/datanucleus-5/src/main/resources/plugin.xml b/persistence/jdo/datanucleus-5/src/main/resources/plugin.xml
index e3e5b74..bb5b2e9 100644
--- a/persistence/jdo/datanucleus-5/src/main/resources/plugin.xml
+++ b/persistence/jdo/datanucleus-5/src/main/resources/plugin.xml
@@ -28,10 +28,10 @@
     <extension point="org.datanucleus.store.rdbms.java_mapping">
         <mapping
             java-type="org.apache.isis.applib.value.Blob"
-            mapping-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuetypes.IsisBlobMapping"/>
+            mapping-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuetypes.applib.IsisBlobMapping"/>
         <mapping
             java-type="org.apache.isis.applib.value.Clob"
-            mapping-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuetypes.IsisClobMapping"/>
+            mapping-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuetypes.applib.IsisClobMapping"/>
         
         <!-- TODO[2249] deprecated
         <mapping
@@ -50,14 +50,14 @@
                 jdbc-type="BLOB"
                 sql-type="BLOB"
                 default="true"
-                column-mapping-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuemappings.ByteArrayBlobRdbmsMapping">
+                column-mapping-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuemappings.applib.ByteArrayBlobRdbmsMapping">
         </mapping>
         <mapping
                 java-type="[Ljava.lang.Byte;"
                 jdbc-type="BLOB"
                 sql-type="BLOB"
                 default="true"
-                column-mapping-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuemappings.ByteArrayBlobRdbmsMapping">
+                column-mapping-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.valuemappings.applib.ByteArrayBlobRdbmsMapping">
         </mapping>
     </extension>
 
@@ -85,6 +85,41 @@
                 converter-name="isis.localresourcepath-string" />
     </extension>
 
+    <extension point="org.datanucleus.java_type">
+        <java-type
+                name="org.apache.isis.applib.services.bookmark.Bookmark"
+                dfg="true"
+                converter-name="isis.bookmark-string" />
+    </extension>
+
+    <extension point="org.datanucleus.java_type">
+        <java-type
+                name="org.apache.isis.schema.chg.v2.ChangesDto"
+                dfg="true"
+                converter-name="isis.changesdtov2-string" />
+    </extension>
+
+    <extension point="org.datanucleus.java_type">
+        <java-type
+                name="org.apache.isis.schema.common.v2.OidDto"
+                dfg="true"
+                converter-name="isis.oiddtov2-string" />
+    </extension>
+
+    <extension point="org.datanucleus.java_type">
+        <java-type
+                name="org.apache.isis.schema.cmd.v2.CommandDto"
+                dfg="true"
+                converter-name="isis.commanddtov2-string" />
+    </extension>
+
+    <extension point="org.datanucleus.java_type">
+        <java-type
+                name="org.apache.isis.schema.ixn.v2.InteractionDto"
+                dfg="true"
+                converter-name="isis.interactiondtov2-string" />
+    </extension>
+
     <!--
     http://www.datanucleus.org/extensions/type_converter.html
     -->
@@ -93,30 +128,63 @@
                 name="isis.password-string"
                 member-type="org.apache.isis.applib.value.Password"
                 datastore-type="java.lang.String"
-                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.IsisPasswordConverter"/>
+                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.applib.IsisPasswordConverter"/>
     </extension>
 
-    <!--
-    http://www.datanucleus.org/extensions/type_converter.html
-    -->
     <extension point="org.datanucleus.type_converter">
         <type-converter
                 name="isis.markup-string"
                 member-type="org.apache.isis.applib.value.Markup"
                 datastore-type="java.lang.String"
-                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.IsisMarkupConverter"/>
+                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.applib.IsisMarkupConverter"/>
     </extension>
 
-
-    <!--
-    http://www.datanucleus.org/extensions/type_converter.html
-    -->
     <extension point="org.datanucleus.type_converter">
         <type-converter
                 name="isis.localresourcepath-string"
                 member-type="org.apache.isis.applib.value.LocalResourcePath"
                 datastore-type="java.lang.String"
-                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.IsisLocalResourcePathConverter"/>
+                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.applib.IsisLocalResourcePathConverter"/>
+    </extension>
+
+    <extension point="org.datanucleus.type_converter">
+        <type-converter
+                name="isis.bookmark-string"
+                member-type="org.apache.isis.applib.services.bookmark.Bookmark"
+                datastore-type="java.lang.String"
+                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.applib.IsisBookmarkConverter"/>
+    </extension>
+
+    <extension point="org.datanucleus.type_converter">
+        <type-converter
+                name="isis.changesdtov2-string"
+                member-type="org.apache.isis.schema.chg.v2.ChangesDto"
+                datastore-type="java.lang.String"
+                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.schema.v2.IsisChangesDtoConverter"/>
+    </extension>
+
+    <extension point="org.datanucleus.type_converter">
+        <type-converter
+                name="isis.oiddtov2-string"
+                member-type="org.apache.isis.schema.common.v2.OidDto"
+                datastore-type="java.lang.String"
+                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.schema.v2.IsisOidDtoConverter"/>
+    </extension>
+
+    <extension point="org.datanucleus.type_converter">
+        <type-converter
+                name="isis.commanddtov2-string"
+                member-type="org.apache.isis.schema.cmd.v2.CommandDto"
+                datastore-type="java.lang.String"
+                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.schema.v2.IsisCommandDtoConverter"/>
+    </extension>
+
+    <extension point="org.datanucleus.type_converter">
+        <type-converter
+                name="isis.interactiondtov2-string"
+                member-type="org.apache.isis.schema.ixn.v2.InteractionDto"
+                datastore-type="java.lang.String"
+                converter-class="org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters.schema.v2.IsisInteractionDtoConverter"/>
     </extension>
 
 </plugin>
diff --git a/api/applib/src/main/java/org/apache/isis/applib/services/background/package-info.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/bookmark/BookmarkPanel.java
similarity index 56%
rename from api/applib/src/main/java/org/apache/isis/applib/services/background/package-info.java
rename to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/bookmark/BookmarkPanel.java
index f63812d..a9611d3 100644
--- a/api/applib/src/main/java/org/apache/isis/applib/services/background/package-info.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/bookmark/BookmarkPanel.java
@@ -17,11 +17,28 @@
  *  under the License.
  */
 
+package org.apache.isis.viewer.wicket.ui.components.scalars.bookmark;
+
+import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelTextFieldParseableAbstract;
+
 /**
- * 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
- *
- *
+ * Panel for rendering scalars of type {@link Bookmark}.
  */
-package org.apache.isis.applib.services.background;
\ No newline at end of file
+public class BookmarkPanel extends ScalarPanelTextFieldParseableAbstract {
+
+    private static final long serialVersionUID = 1L;
+
+
+    public BookmarkPanel(final String id, final ScalarModel scalarModel) {
+        super(id, scalarModel);
+    }
+
+    @Override
+    protected String getScalarPanelType() {
+        return "bookmarkPanel";
+    }
+
+
+
+}
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/bookmark/BookmarkPanelFactory.java
similarity index 53%
copy from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
copy to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/bookmark/BookmarkPanelFactory.java
index 3daffaa..c9e3f80 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/bookmark/BookmarkPanelFactory.java
@@ -16,28 +16,27 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
 
-import org.datanucleus.store.types.converters.TypeConverter;
+package org.apache.isis.viewer.wicket.ui.components.scalars.bookmark;
 
-import org.apache.isis.applib.value.Markup;
+import org.apache.wicket.Component;
 
-public class IsisMarkupConverter implements TypeConverter<Markup, String>{
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.core.metamodel.facets.objectvalue.multiline.MultiLineFacet;
+import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ComponentFactoryScalarAbstract;
+import org.apache.isis.viewer.wicket.ui.components.scalars.string.MultiLineStringPanel;
+
+public class BookmarkPanelFactory extends ComponentFactoryScalarAbstract {
 
     private static final long serialVersionUID = 1L;
 
-    @Override
-    public String toDatastoreType(final Markup memberValue) {
-        return memberValue != null
-                ? memberValue.asHtml()
-                        : null;
+    public BookmarkPanelFactory() {
+        super(BookmarkPanel.class, Bookmark.class);
     }
 
     @Override
-    public Markup toMemberType(final String datastoreValue) {
-        return datastoreValue != null
-                ? new Markup(datastoreValue)
-                        : null;
+    public Component createComponent(final String id, final ScalarModel scalarModel) {
+        return new BookmarkPanel(id, scalarModel);
     }
-
 }
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/oiddto/OidDtoPanel.java
similarity index 59%
copy from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
copy to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/oiddto/OidDtoPanel.java
index 3daffaa..0ecdf92 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/oiddto/OidDtoPanel.java
@@ -16,28 +16,29 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
 
-import org.datanucleus.store.types.converters.TypeConverter;
+package org.apache.isis.viewer.wicket.ui.components.scalars.oiddto;
 
-import org.apache.isis.applib.value.Markup;
+import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ScalarPanelTextFieldParseableAbstract;
 
-public class IsisMarkupConverter implements TypeConverter<Markup, String>{
+/**
+ * Panel for rendering scalars of type {@link org.apache.isis.schema.common.v2.OidDto}.
+ */
+public class OidDtoPanel extends ScalarPanelTextFieldParseableAbstract {
 
     private static final long serialVersionUID = 1L;
 
-    @Override
-    public String toDatastoreType(final Markup memberValue) {
-        return memberValue != null
-                ? memberValue.asHtml()
-                        : null;
+
+    public OidDtoPanel(final String id, final ScalarModel scalarModel) {
+        super(id, scalarModel);
     }
 
     @Override
-    public Markup toMemberType(final String datastoreValue) {
-        return datastoreValue != null
-                ? new Markup(datastoreValue)
-                        : null;
+    protected String getScalarPanelType() {
+        return "oidDtoPanel";
     }
 
+
+
 }
diff --git a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/oiddto/OidDtoPanelFactory.java
similarity index 59%
rename from persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
rename to viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/oiddto/OidDtoPanelFactory.java
index 3daffaa..4d69b5d 100644
--- a/persistence/jdo/datanucleus-5/src/main/java/org/apache/isis/persistence/jdo/datanucleus5/datanucleus/typeconverters/IsisMarkupConverter.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/components/scalars/oiddto/OidDtoPanelFactory.java
@@ -16,28 +16,25 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.persistence.jdo.datanucleus5.datanucleus.typeconverters;
 
-import org.datanucleus.store.types.converters.TypeConverter;
+package org.apache.isis.viewer.wicket.ui.components.scalars.oiddto;
 
-import org.apache.isis.applib.value.Markup;
+import org.apache.wicket.Component;
 
-public class IsisMarkupConverter implements TypeConverter<Markup, String>{
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.viewer.wicket.model.models.ScalarModel;
+import org.apache.isis.viewer.wicket.ui.components.scalars.ComponentFactoryScalarAbstract;
+
+public class OidDtoPanelFactory extends ComponentFactoryScalarAbstract {
 
     private static final long serialVersionUID = 1L;
 
-    @Override
-    public String toDatastoreType(final Markup memberValue) {
-        return memberValue != null
-                ? memberValue.asHtml()
-                        : null;
+    public OidDtoPanelFactory() {
+        super(OidDtoPanel.class, Bookmark.class);
     }
 
     @Override
-    public Markup toMemberType(final String datastoreValue) {
-        return datastoreValue != null
-                ? new Markup(datastoreValue)
-                        : null;
+    public Component createComponent(final String id, final ScalarModel scalarModel) {
+        return new OidDtoPanel(id, scalarModel);
     }
-
 }
diff --git a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/FormExecutorDefault.java b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/FormExecutorDefault.java
index 413ab0d..fcb048b 100644
--- a/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/FormExecutorDefault.java
+++ b/viewers/wicket/ui/src/main/java/org/apache/isis/viewer/wicket/ui/panels/FormExecutorDefault.java
@@ -123,12 +123,6 @@ implements FormExecutor {
                 return false;
             }
 
-            val commandContext = currentCommandContext().orElse(null);
-            if (commandContext != null) {
-                command = commandContext.getCommand();
-                command.internal().setExecutor(Command.Executor.USER);
-            }
-
 
             //
             // the following line will (attempt to) invoke the action, and will in turn either:
@@ -248,11 +242,7 @@ implements FormExecutor {
             // irrespective, capture error in the Command, and propagate
             if (command != null) {
                 
-                val stackTraceAsString = 
-                _Exceptions.streamStacktraceLines(ex, 1000)
-                .collect(Collectors.joining("\n"));
-                
-                command.internal().setException(stackTraceAsString);
+                command.internal().setException(ex);
                 
                 //XXX legacy of
                 //command.internal().setException(Throwables.getStackTraceAsString(ex));
diff --git a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
index 0f126d9..0b303db 100644
--- a/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
+++ b/viewers/wicket/viewer/src/main/java/org/apache/isis/viewer/wicket/viewer/registries/components/ComponentFactoryRegistrarDefault.java
@@ -29,6 +29,7 @@ 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.core.commons.internal.base._NullSafe;
 import org.apache.isis.viewer.wicket.ui.ComponentFactory;
 import org.apache.isis.viewer.wicket.ui.app.registry.ComponentFactoryRegistrar;
@@ -54,6 +55,7 @@ import org.apache.isis.viewer.wicket.ui.components.footer.FooterPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.header.HeaderPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.property.PropertyEditFormPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.property.PropertyEditPanelFactory;
+import org.apache.isis.viewer.wicket.ui.components.scalars.bookmark.BookmarkPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.scalars.isisapplib.IsisBlobPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.scalars.isisapplib.IsisClobPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.scalars.isisapplib.IsisPasswordPanelFactory;
@@ -70,6 +72,7 @@ import org.apache.isis.viewer.wicket.ui.components.scalars.jodatime.JodaDateTime
 import org.apache.isis.viewer.wicket.ui.components.scalars.jodatime.JodaLocalDatePanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.scalars.jodatime.JodaLocalDateTimePanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.scalars.markup.MarkupPanelFactories;
+import org.apache.isis.viewer.wicket.ui.components.scalars.oiddto.OidDtoPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.scalars.primitive.BooleanPanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.scalars.primitive.BytePanelFactory;
 import org.apache.isis.viewer.wicket.ui.components.scalars.primitive.CharacterPanelFactory;
@@ -228,6 +231,7 @@ public class ComponentFactoryRegistrarDefault implements ComponentFactoryRegistr
 
         componentFactories.add(new StringPanelFactory());
 
+
         // work-in-progress
         // componentFactories.add(new JavaAwtImagePanelFactory());
         componentFactories.add(new JavaUtilDatePanelFactory());
@@ -237,6 +241,9 @@ public class ComponentFactoryRegistrarDefault implements ComponentFactoryRegistr
 
         componentFactories.add(new IsisPasswordPanelFactory());
 
+        componentFactories.add(new BookmarkPanelFactory());
+        componentFactories.add(new OidDtoPanelFactory());
+
         componentFactories.add(new IsisBlobPanelFactory());
         componentFactories.add(new IsisClobPanelFactory());