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 2021/09/10 06:30:44 UTC

[isis] branch ISIS-2735-command-log updated: ISIS-2735: wip - fixes up jdo PublishedCommandForJdo

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

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


The following commit(s) were added to refs/heads/ISIS-2735-command-log by this push:
     new 85ddb0e  ISIS-2735: wip - fixes up jdo PublishedCommandForJdo
85ddb0e is described below

commit 85ddb0e539cd80716c8691cfb3f68ea2fa5c511f
Author: danhaywood <da...@haywood-associates.co.uk>
AuthorDate: Fri Sep 10 07:30:31 2021 +0100

    ISIS-2735: wip - fixes up jdo PublishedCommandForJdo
---
 .../system/hooks/HasInteractionId_see-also.adoc    |   2 +-
 .../roles-mixins-contributees/contributee.adoc     |   2 +-
 .../modules/fun/pages/overview/events.adoc         |   2 +-
 .../modules/config/pages/sections/isis.applib.adoc |  42 +-
 .../domain/_commands/ExposePersistedCommands.java  |   1 -
 .../ExposePersistedCommands_commands.java          |   1 -
 .../applib/IsisModuleExtCommandLogApplib.java      |  40 +-
 .../commandlog/applib/dom/CommandModel.java        |  77 ---
 .../dom/{Command.java => PublishedCommand.java}    | 224 +++++--
 .../dom/PublishedCommand.layout.fallback.xml}      |   0
 .../commandlog/applib/dom/PublishedCommand.png}    | Bin
 ...sitory.java => PublishedCommandRepository.java} |  49 +-
 .../mixins/PublishedCommand_childCommands.java}    |  26 +-
 .../mixins/PublishedCommand_openResultObject.java} |  22 +-
 .../mixins/PublishedCommand_openTargetObject.java} |  32 +-
 .../applib/dom/mixins/PublishedCommand_retry.java  |  59 ++
 .../mixins/PublishedCommand_siblingCommands.java}  |  27 +-
 .../commandlog/jdo/CommandSubscriberForJdo.java    |   8 +-
 .../commandlog/jdo/IsisModuleExtCommandLogJdo.java |   7 +-
 .../commandlog/jdo/entities/CommandJdo.java        | 647 ---------------------
 .../jdo/entities/CommandJdoRepository.java         | 129 ++--
 .../commandlog/jdo/entities/CommandJdo_retry.java  |  70 ---
 .../jdo/entities/PublishedCommandForJdo.java       | 479 +++++++++++++++
 .../jdo/mixins/HasInteractionId_command.java       |   8 +-
 .../mixins/HasUsername_recentCommandsByUser.java   |   6 +-
 .../jdo/mixins/Object_recentCommands.java          |   4 +-
 .../extensions/commandlog/jdo/mixins/T_recent.java |   8 +-
 .../commandlog/jdo/ui/CommandServiceMenu.java      |   8 +-
 .../commandlog/jpa/entities/CommandJpa.java        |  16 +-
 .../jpa/entities/CommandJpaRepository.java         |   5 +-
 .../primary/restapi/CommandRetrievalService.java   |   8 +-
 .../primary/spiimpl/CaptureResultOfCommand.java    |   1 -
 .../primary/ui/CommandReplayOnPrimaryService.java  |   9 +-
 .../secondary/analyser/CommandReplayAnalyser.java  |   2 -
 .../analyser/CommandReplayAnalyserException.java   |   1 -
 .../analyser/CommandReplayAnalyserResult.java      |   1 -
 .../analysis/CommandReplayAnalysisService.java     |   1 -
 .../secondary/fetch/CommandFetcher.java            |   1 -
 .../jobcallables/ReplicateAndRunCommands.java      |  16 +-
 .../secondary/mixins/CommandJdo_replayQueue.java   |   8 +-
 .../secondary/mixins/CommandModel_exclude.java     |   1 -
 .../ui/CommandReplayOnSecondaryService.java        |  10 +-
 ...Test.java => PublishedCommandFetcher_Test.java} |   2 +-
 .../secman/applib/role/dom/ApplicationRole.java    |   2 +-
 .../secman/jdo/role/dom/ApplicationRole.java       |   4 +-
 .../secman/jpa/role/dom/ApplicationRole.java       |   4 +-
 .../pages/hints-and-tips/jdoql-and-timestamps.adoc |   2 +-
 47 files changed, 965 insertions(+), 1109 deletions(-)

diff --git a/antora/components/refguide-index/modules/applib/pages/index/mixins/system/hooks/HasInteractionId_see-also.adoc b/antora/components/refguide-index/modules/applib/pages/index/mixins/system/hooks/HasInteractionId_see-also.adoc
index 3a58661..e473ba0 100644
--- a/antora/components/refguide-index/modules/applib/pages/index/mixins/system/hooks/HasInteractionId_see-also.adoc
+++ b/antora/components/refguide-index/modules/applib/pages/index/mixins/system/hooks/HasInteractionId_see-also.adoc
@@ -8,7 +8,7 @@
 Some of the extension/mapping modules that have entities that implement this mixee are:
 
 * xref:security:audit-trail:about.adoc[Auditer] module: `AuditEntry` entity
-* xref:userguide:command-log:about.adoc[Command Log] module: `CommandJdo` entity
+* xref:userguide:command-log:about.adoc[Command Log] module: `PublishedCommandForJdo` entity
 * xref:mappings:outbox-publisher:about.adoc[Outbox Publisher] module: `OutboxEvent` entity
 
 
diff --git a/antora/components/refguide/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc b/antora/components/refguide/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc
index 8fa31f3..763c42e 100644
--- a/antora/components/refguide/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc
+++ b/antora/components/refguide/modules/applib-classes/pages/roles-mixins-contributees/contributee.adoc
@@ -36,7 +36,7 @@ Some of the extension/mapping modules that have domain entity/ies that implement
 
 * xref:security:ROOT:about.adoc[SecMan extension]: `ApplicationUser` entity
 * xref:security:audit-trail:about.adoc[Auditer] module: `AuditEntry` entity
-* xref:userguide:command-log:about.adoc[Command Log] module: `CommandJdo` entity
+* xref:userguide:command-log:about.adoc[Command Log] module: `PublishedCommandForJdo` entity
 * xref:mappings:outbox-publisher:about.adoc[Outbox Publisher] module: `OutboxEvent` entity
 * xref:security:session-log:about.adoc[Session Log] module: `SessionLogEntry` entity
 * xref:subdomains:settings:about.adoc[Settings] module: `UserSettingJdo` entity
diff --git a/antora/components/userguide/modules/fun/pages/overview/events.adoc b/antora/components/userguide/modules/fun/pages/overview/events.adoc
index 1d4a366..7916353 100644
--- a/antora/components/userguide/modules/fun/pages/overview/events.adoc
+++ b/antora/components/userguide/modules/fun/pages/overview/events.adoc
@@ -18,7 +18,7 @@ These are explored in the sections below.
 Domain events are fired -- through the internal xref:refguide:applib:index/services/eventbus/EventBusService.adoc[event bus] -- for every user interaction with each object member (property, collection or action).
 
 By default, rendering a property causes a `PropertyDomainEvent` to be fired, though the xref:refguide:applib:index/annotation/Property.adoc#domainEvent[@Property#domainEvent()] attribute allows a custom subclass to be specified if necessary.
-Similarly, rendering a collection causes a `DomainEvent` to be fired, and rendering an action causes an `ActionDomainEvent` to be fired.
+Similarly, rendering a collection causes a `DomainEvent` to be fired, and rendering an action causes an `DomainEvent` to be fired.
 
 In fact, each event can be fired up to five times, with the event's `getEventPhase()` method indicating to the subscriber the phase:
 
diff --git a/core/config/src/main/adoc/modules/config/pages/sections/isis.applib.adoc b/core/config/src/main/adoc/modules/config/pages/sections/isis.applib.adoc
index 60808a8..464db72 100644
--- a/core/config/src/main/adoc/modules/config/pages/sections/isis.applib.adoc
+++ b/core/config/src/main/adoc/modules/config/pages/sections/isis.applib.adoc
@@ -72,7 +72,7 @@ isis.applib.annotation. +
 action-layout.css-class-fa. +
 patterns-as-map
 
-| 
+|
 | null
 
 
@@ -97,7 +97,7 @@ isis.applib.annotation. +
 action-layout.css-class. +
 patterns-as-map
 
-| 
+|
 | null
 
 
@@ -106,8 +106,8 @@ patterns-as-map
 isis.applib.annotation.action. +
 command-publishing
 
-| 
-| TODO[2464] semantic renaming audit/dispatch -> publishing The default for whether action invocations should be reified as a ``Command``, to be sent to any registered ``CommandSubscriber``s, either for auditing or for replayed against a secondary system, eg for regression testing.
+|
+| TODO[2464] semantic renaming audit/dispatch -> publishing The default for whether action invocations should be reified as a ``PublishedCommand``, to be sent to any registered ``CommandSubscriber``s, either for auditing or for replayed against a secondary system, eg for regression testing.
 
 This setting can be overridden on a case-by-case basis using ``Action#commandPublishing()``.
 
@@ -118,7 +118,7 @@ isis.applib.annotation.action. +
 domain-event.post-for-default
 
 |  true
-| Influences whether an ``ActionDomainEvent`` should be published (on the internal ``EventBusService``) whenever an action is being interacted with.
+| Influences whether an ``DomainEvent`` should be published (on the internal ``EventBusService``) whenever an action is being interacted with.
 
 Up to five different events can be fired during an interaction, with the event's phase determining which (hide, disable, validate, executing and executed). Subscribers can influence the behaviour at each of these phases.
 
@@ -134,7 +134,7 @@ The algorithm for determining whether (and what type of) an event is actually se
 isis.applib.annotation.action. +
 execution-publishing
 
-| 
+|
 | TODO[2464] semantic renaming audit/dispatch -> publishing The default for whether action invocations should be sent through to the ``ExecutionSubscriber`` for publishing.
 
 The service's publish method is called only once per transaction, with ``Execution`` collecting details of the identity of the target object, the action invoked, the action arguments and the returned object (if any).
@@ -147,7 +147,7 @@ This setting can be overridden on a case-by-case basis using ``Action#executionD
 isis.applib.annotation.action. +
 explicit
 
-| 
+|
 | Whether or not a public method needs to be annotated with @``Action`` in order to be picked up as an action in the metamodel.
 
 
@@ -156,7 +156,7 @@ explicit
 isis.applib.annotation. +
 collection-layout.default-view
 
-| 
+|
 | Defines the initial view to display collections when rendered.
 
 The value of this can be overridden on a case-by-case basis using ``CollectionLayout#defaultView()``. Note that this default configuration property is an enum and so defines only a fixed number of values, whereas the annotation returns a string; this is to allow for flexibility that individual viewers might support their own additional types. For example, the Wicket viewer supports
@@ -179,7 +179,7 @@ isis.applib.annotation.collection. +
 domain-event.post-for-default
 
 |  true
-| Influences whether an ``CollectionDomainEvent`` should be published (on the internal ``EventBusService``) whenever a collection is being interacted with.
+| Influences whether an ``DomainEvent`` should be published (on the internal ``EventBusService``) whenever a collection is being interacted with.
 
 Up to two different events can be fired during an interaction, with the event's phase determining which (hide, disable)Subscribers can influence the behaviour at each of these phases.
 
@@ -197,7 +197,7 @@ domain-object-layout. +
 css-class-ui-event. +
 post-for-default
 
-| 
+|
 | Influences whether an ``CssClassUiEvent`` should be published (on the internal ``EventBusService``) whenever a domain object is about to be rendered in the UI - thereby allowing subscribers to optionally ``CssClassUiEvent#setCssClass(String)`` change) the CSS classes that are used.
 
 The algorithm for determining whether (and what type of) an event is sent depends on the value of the ``DomainObjectLayout#cssClassUiEvent()`` @DomainObjectLayout(cssClassEvent=...)} for the domain object in question.
@@ -215,7 +215,7 @@ isis.applib.annotation. +
 domain-object-layout.icon-ui-event. +
 post-for-default
 
-| 
+|
 | Influences whether an ``IconUiEvent`` should be published (on the internal ``EventBusService``) whenever a domain object is about to be rendered in the UI - thereby allowing subscribers to optionally ``IconUiEvent#setIconName(String)`` change) the icon that is used.
 
 The algorithm for determining whether (and what type of) an event is sent depends on the value of the ``DomainObjectLayout#iconUiEvent()`` @DomainObjectLayout(iconEvent=...)} for the domain object in question.
@@ -233,7 +233,7 @@ isis.applib.annotation. +
 domain-object-layout. +
 layout-ui-event.post-for-default
 
-| 
+|
 | Influences whether an ``LayoutUiEvent`` should be published (on the internal ``EventBusService``) whenever a domain object is about to be rendered in the UI - thereby allowing subscribers to optionally ``LayoutUiEvent#setLayout(String)`` change) the layout that is used.
 
 If a different layout value has been set, then a layout in the form ``xml`` use used (where ``zzz`` is the name of the layout).
@@ -264,7 +264,7 @@ isis.applib.annotation. +
 domain-object-layout. +
 title-ui-event.post-for-default
 
-| 
+|
 | Influences whether an ``TitleUiEvent`` should be published (on the internal ``EventBusService``) whenever a domain object is about to be rendered in the UI - thereby allowing subscribers to optionally ``TitleUiEvent#setTitle(String)`` change) the title that is used.
 
 The algorithm for determining whether (and what type of) an event is sent depends on the value of the ``DomainObjectLayout#titleUiEvent()`` @DomainObjectLayout(titleEvent=...)} for the domain object in question.
@@ -298,7 +298,7 @@ The algorithm for determining whether (and what type of) an event is sent depend
 isis.applib.annotation. +
 domain-object.editing
 
-| 
+|
 | The default for whether the properties of domain objects can be edited, or whether instead they can be modified only using actions (or programmatically as a side-effect of actions on other objects).
 
 This setting can be overridden on a case-by-case basis using DomainObject#getEditing()
@@ -310,7 +310,7 @@ isis.applib.annotation. +
 domain-object. +
 entity-change-publishing
 
-| 
+|
 | TODO[2464] semantic renaming audit/dispatch -> publishing The default for whether _domain entities_ should be audited or not (meaning that any changes are sent through to ``EntityChangesSubscriber``s and sent through to ``EntityPropertyChangeSubscriber``.
 
 This setting can be overridden on a case-by-case basis using ``DomainObject#entityChangePublishing()``
@@ -439,7 +439,7 @@ Note: this applies only to domain entities, not to view models.
 isis.applib.annotation. +
 parameter-layout.label-position
 
-| 
+|
 | Defines the default position for the label for an action parameter.
 
 Can be overridden on a case-by-case basis using ``ParameterLayout#labelPosition()``.
@@ -452,7 +452,7 @@ If left as ``LabelPosition#NOT_SPECIFIED`` and not overridden, then the position
 isis.applib.annotation. +
 property-layout.label-position
 
-| 
+|
 | Defines the default position for the label for a domain object property.
 
 Can be overridden on a case-by-case basis using ``ParameterLayout#labelPosition()``.
@@ -465,8 +465,8 @@ If left as ``LabelPosition#NOT_SPECIFIED`` and not overridden, then the position
 isis.applib.annotation.property. +
 command-publishing
 
-| 
-| TODO[2464] semantic renaming audit/dispatch -> publishing The default for whether property edits should be reified as a ``Command``, to be sent to any registered ``CommandSubscriber``s, either for auditing or for replayed against a secondary system, eg for regression testing.
+|
+| TODO[2464] semantic renaming audit/dispatch -> publishing The default for whether property edits should be reified as a ``PublishedCommand``, to be sent to any registered ``CommandSubscriber``s, either for auditing or for replayed against a secondary system, eg for regression testing.
 
 This setting can be overridden on a case-by-case basis using ``Property#commandDispatch()``.
 
@@ -493,7 +493,7 @@ The algorithm for determining whether (and what type of) an event is actually se
 isis.applib.annotation.property. +
 execution-publishing
 
-| 
+|
 | TODO[2464] semantic renaming audit/dispatch -> publishing The default for whether property edits should be sent through to the ``ExecutionSubscriber`` for publishing.
 
 The service's publish method is called only once per transaction, with ``Execution`` collecting details of the identity of the target object, the property edited, and the new value of the property.
@@ -574,7 +574,7 @@ isis.applib.annotation.view-model. +
 validation.semantic-checking. +
 enable
 
-| 
+|
 | Whether to check for inconsistencies between the usage of ``DomainObject``, ``ViewModel``, ``DomainObjectLayout`` and ``ViewModelLayout``.
 
 
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands.java
index 51254d5..70877cc 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands.java
@@ -25,7 +25,6 @@ import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.services.tablecol.TableColumnOrderForCollectionTypeAbstract;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
 
 /**
  * Marker interface for mixins to contribute to.
diff --git a/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands_commands.java b/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands_commands.java
index 767f8f9..f544f65 100644
--- a/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands_commands.java
+++ b/examples/demo/domain/src/main/java/demoapp/dom/domain/_commands/ExposePersistedCommands_commands.java
@@ -25,7 +25,6 @@ import javax.inject.Inject;
 import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
 
 import lombok.RequiredArgsConstructor;
 
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java
index 2206d1f..fc2a2f3 100644
--- a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/IsisModuleExtCommandLogApplib.java
@@ -18,32 +18,50 @@
  */
 package org.apache.isis.extensions.commandlog.applib;
 
-import org.apache.isis.testing.fixtures.applib.modules.ModuleWithFixtures;
 
-public interface IsisModuleExtCommandLogApplib
-extends ModuleWithFixtures {
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
 
-    String NAMESPACE = "isis.ext.commandLog";
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
+import org.apache.isis.testing.fixtures.applib.IsisModuleTestingFixturesApplib;
 
-    abstract class TitleUiEvent<S>
+/**
+ * @since 2.0 {@index}
+ */
+@Configuration
+@Import({
+        // modules
+        IsisModuleTestingFixturesApplib.class,
+
+
+        // @Services
+        PublishedCommand.TitleProvider.class,
+        PublishedCommand.TableColumnOrderDefault.class,
+
+})
+public class IsisModuleExtCommandLogApplib {
+
+    public static final String NAMESPACE = "isis.ext.commandLog";
+
+    public static abstract class TitleUiEvent<S>
         extends org.apache.isis.applib.events.ui.TitleUiEvent<S> { }
 
-    abstract class IconUiEvent<S>
+    public static abstract class IconUiEvent<S>
         extends org.apache.isis.applib.events.ui.IconUiEvent<S> { }
 
-    abstract class CssClassUiEvent<S>
+    public static abstract class CssClassUiEvent<S>
         extends org.apache.isis.applib.events.ui.CssClassUiEvent<S> { }
 
-    abstract class LayoutUiEvent<S>
+    public static abstract class LayoutUiEvent<S>
         extends org.apache.isis.applib.events.ui.LayoutUiEvent<S> { }
 
-    abstract class ActionDomainEvent<S>
+    public static abstract class ActionDomainEvent<S>
         extends org.apache.isis.applib.events.domain.ActionDomainEvent<S> { }
 
-    abstract class CollectionDomainEvent<S,T>
+    public static abstract class CollectionDomainEvent<S,T>
         extends org.apache.isis.applib.events.domain.CollectionDomainEvent<S,T> { }
 
-    abstract class PropertyDomainEvent<S,T>
+    public static abstract class PropertyDomainEvent<S,T>
         extends org.apache.isis.applib.events.domain.PropertyDomainEvent<S,T> { }
 
 }
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandModel.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandModel.java
deleted file mode 100644
index de23ac8..0000000
--- a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandModel.java
+++ /dev/null
@@ -1,77 +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.extensions.commandlog.applib.dom;
-
-import java.sql.Timestamp;
-import java.util.UUID;
-
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.command.CommandOutcomeHandler;
-import org.apache.isis.applib.services.commanddto.HasCommandDto;
-import org.apache.isis.applib.util.ObjectContracts;
-
-public interface CommandModel
-extends
-    HasCommandDto,
-    Comparable<CommandModel> {
-
-
-
-    Bookmark getResult();
-
-    String getException();
-
-    Timestamp getStartedAt();
-    Timestamp getCompletedAt();
-    Timestamp getTimestamp();
-
-    Bookmark getTarget();
-
-    String getLogicalMemberIdentifier();
-
-    String getUsername();
-
-    void saveAnalysis(String analysis);
-
-    UUID getInteractionId();
-
-    ReplayState getReplayState();
-
-    CommandOutcomeHandler outcomeHandler();
-
-    void setReplayState(ReplayState excluded);
-
-    default String toFriendlyString() {
-        return ObjectContracts
-                .toString("interactionId", CommandModel::getInteractionId)
-                .thenToString("username", CommandModel::getUsername)
-                .thenToString("timestamp", CommandModel::getTimestamp)
-                .thenToString("target", CommandModel::getTarget)
-                .thenToString("logicalMemberIdentifier", CommandModel::getLogicalMemberIdentifier)
-                .thenToStringOmitIfAbsent("startedAt", CommandModel::getStartedAt)
-                .thenToStringOmitIfAbsent("completedAt", CommandModel::getCompletedAt)
-                .toString(this);
-    }
-
-    @Override
-    default int compareTo(final CommandModel other) {
-        return this.getTimestamp().compareTo(other.getTimestamp());
-    }
-
-}
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/Command.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommand.java
similarity index 79%
rename from extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/Command.java
rename to extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommand.java
index 27b606f..de8be99 100644
--- a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/Command.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommand.java
@@ -35,8 +35,9 @@ import java.util.function.Consumer;
 import javax.annotation.Nullable;
 
 import org.springframework.context.event.EventListener;
-import org.springframework.stereotype.Service;
+import org.springframework.stereotype.Component;
 
+import org.apache.isis.applib.annotation.BookmarkPolicy;
 import org.apache.isis.applib.annotation.Domain;
 import org.apache.isis.applib.annotation.DomainObject;
 import org.apache.isis.applib.annotation.DomainObjectLayout;
@@ -53,12 +54,12 @@ import org.apache.isis.applib.annotation.Where;
 import org.apache.isis.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
 import org.apache.isis.applib.mixins.system.DomainChangeRecord;
 import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.CommandOutcomeHandler;
 import org.apache.isis.applib.services.commanddto.conmap.UserDataKeys;
 import org.apache.isis.applib.services.tablecol.TableColumnOrderForCollectionTypeAbstract;
 import org.apache.isis.applib.util.ObjectContracts;
 import org.apache.isis.applib.util.TitleBuffer;
-import org.apache.isis.commons.functional.Result;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
 import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
@@ -67,33 +68,87 @@ import org.apache.isis.extensions.commandlog.applib.util.StringUtils;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.MapDto;
 
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 import lombok.NonNull;
 import lombok.val;
 
+/**
+ * A persistent representation of a {@link Command}.
+ *
+ * <p>
+ *     Use cases requiring persistence including auditing, and for replay of
+ *     commands for regression testing purposes.
+ * </p>
+ *
+ * @since 2.0 {@index}
+ */
 @DomainObject(
-        logicalTypeName = Command.LOGICAL_TYPE_NAME,
-        editing = Editing.DISABLED
+        editing = Editing.DISABLED,
+        logicalTypeName = PublishedCommand.LOGICAL_TYPE_NAME
 )
 @DomainObjectLayout(
-        titleUiEvent = Command.TitleUiEvent.class,
-        iconUiEvent = Command.IconUiEvent.class,
-        cssClassUiEvent = Command.CssClassUiEvent.class,
-        layoutUiEvent = Command.LayoutUiEvent.class
+        bookmarking = BookmarkPolicy.AS_ROOT,
+        cssClassUiEvent = PublishedCommand.CssClassUiEvent.class,
+        iconUiEvent = PublishedCommand.IconUiEvent.class,
+        layoutUiEvent = PublishedCommand.LayoutUiEvent.class,
+        titleUiEvent = PublishedCommand.TitleUiEvent.class
 )
-public abstract class Command implements DomainChangeRecord {
+@NoArgsConstructor(access = AccessLevel.PROTECTED)
+public abstract class PublishedCommand implements DomainChangeRecord {
+
+    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogApplib.NAMESPACE + ".PublishedCommand";
+
+    public static final String NAMED_QUERY_FIND_BY_INTERACTION_ID = "PublishedCommand.findByInteractionId";
+    public static final String NAMED_QUERY_FIND_BY_PARENT = "PublishedCommand.findByParent";
+    public static final String NAMED_QUERY_FIND_CURRENT = "PublishedCommand.findCurrent";
+    public static final String NAMED_QUERY_FIND_COMPLETED = "PublishedCommand.findCompleted";
+    public static final String NAMED_QUERY_FIND_RECENT_BY_TARGET = "PublishedCommand.findRecentByTarget";
+    public static final String NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_BETWEEEN = "PublishedCommand.findByTargetAndTimestampBetween";
+    public static final String NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_AFTER = "PublishedCommand.findByTargetAndTimestampAfter";
+    public static final String NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_BEFORE = "PublishedCommand.findByTargetAndTimestampBefore";
+    public static final String NAMED_QUERY_FIND_BY_TARGET = "PublishedCommand.findByTarget";
+    public static final String NAMED_QUERY_FIND_BY_TIMESTAMP_BETWEEN = "PublishedCommand.findByTimestampBetween";
+    public static final String NAMED_QUERY_FIND_BY_TIMESTAMP_AFTER = "PublishedCommand.findByTimestampAfter";
+    public static final String NAMED_QUERY_FIND_BY_TIMESTAMP_BEFORE = "PublishedCommand.findByTimestampBefore";
+    public static final String NAMED_QUERY_FIND = "PublishedCommand.find";
+    public static final String NAMED_QUERY_FIND_BY_USERNAME = "PublishedCommand.findRecentByUsername";
+    public static final String NAMED_QUERY_FIND_FIRST = "PublishedCommand.findFirst";
+    public static final String NAMED_QUERY_FIND_SINCE = "PublishedCommand.findSince";
+    /**
+     * most recent (replayed) command previously replicated from primary to secondary.
+     *
+     * This should always exist except for the very first times (after restored the prod DB to secondary).
+     */
+    public static final String NAMED_QUERY_FIND_MOST_RECENT_REPLAYED = "PublishedCommand.findMostRecentReplayed";
+    /**
+     *  the most recent completed command, as queried on the secondary, corresponding to the
+     *  last command run on primary before the production database was restored to the secondary
+     */
+    public static final String NAMED_QUERY_FIND_MOST_RECENT_COMPLETED = "PublishedCommand.findMostRecentCompleted";
+    public static final String NAMED_QUERY_FIND_NOT_YET_REPLAYED = "PublishedCommand.findNotYetReplayed";
+
 
-    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogApplib.NAMESPACE + ".Command";
+    // EVENTS
 
-    public static class TitleUiEvent extends IsisModuleExtCommandLogApplib.TitleUiEvent<Command> { }
-    public static class IconUiEvent extends IsisModuleExtCommandLogApplib.IconUiEvent<Command> { }
-    public static class CssClassUiEvent extends IsisModuleExtCommandLogApplib.CssClassUiEvent<Command> { }
-    public static class LayoutUiEvent extends IsisModuleExtCommandLogApplib.LayoutUiEvent<Command> { }
+    public static class TitleUiEvent extends IsisModuleExtCommandLogApplib.TitleUiEvent<PublishedCommand> { }
+    public static class IconUiEvent extends IsisModuleExtCommandLogApplib.IconUiEvent<PublishedCommand> { }
+    public static class CssClassUiEvent extends IsisModuleExtCommandLogApplib.CssClassUiEvent<PublishedCommand> { }
+    public static class LayoutUiEvent extends IsisModuleExtCommandLogApplib.LayoutUiEvent<PublishedCommand> { }
 
-    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtCommandLogApplib.PropertyDomainEvent<Command, T> { }
-    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtCommandLogApplib.CollectionDomainEvent<Command, T> { }
+    public static abstract class PropertyDomainEvent<T> extends IsisModuleExtCommandLogApplib.PropertyDomainEvent<PublishedCommand, T> { }
+    public static abstract class CollectionDomainEvent<T> extends IsisModuleExtCommandLogApplib.CollectionDomainEvent<PublishedCommand, T> { }
 
 
-    public Command(final org.apache.isis.applib.services.command.Command command) {
+
+    // CONSTRUCTORS
+
+    /**
+     * Intended for use on primary system.
+     *
+     * @param command
+     */
+    public PublishedCommand(final Command command) {
 
         setInteractionId(command.getInteractionId());
         setUsername(command.getUsername());
@@ -120,7 +175,7 @@ public abstract class Command implements DomainChangeRecord {
      * @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 Command(
+    public PublishedCommand(
             final CommandDto commandDto,
             final ReplayState replayState,
             final int targetIndex) {
@@ -158,7 +213,11 @@ public abstract class Command implements DomainChangeRecord {
                 .ifPresent(consume);
     }
 
-    @Service
+
+    // TITLE
+
+    @Component
+    @javax.annotation.Priority(PriorityPrecedence.LATE)
     public static class TitleProvider {
 
         @EventListener(TitleUiEvent.class)
@@ -169,7 +228,7 @@ public abstract class Command implements DomainChangeRecord {
             ev.setTitle(title(ev.getSource()));
         }
 
-        private static String title(final @Nullable Command source) {
+        private static String title(final @Nullable PublishedCommand source) {
             if(source == null) {
                 return "???"; // shouldn't happen.
             }
@@ -185,6 +244,8 @@ public abstract class Command implements DomainChangeRecord {
     }
 
 
+    // (CHANGE) TYPE
+
     @DomainChangeRecord.ChangeTypeMeta
     @Override
     public DomainChangeRecord.ChangeType getType() {
@@ -192,24 +253,31 @@ public abstract class Command implements DomainChangeRecord {
     }
 
 
+    // INTERACTION ID
+
     @DomainChangeRecord.InteractionId
     @Override
     public abstract UUID getInteractionId();
     public abstract void setInteractionId(UUID interactionId);
 
 
+    // USER NAME
+
     @DomainChangeRecord.Username
     @Override
     public abstract String getUsername();
     public abstract void setUsername(String username);
 
 
+    // TIMESTAMP
+
     @DomainChangeRecord.TimestampMeta
     @Override
     public abstract java.sql.Timestamp getTimestamp();
     public abstract void setTimestamp(java.sql.Timestamp timestamp);
 
 
+    // REPLAY STATE
 
     @Property(
             domainEvent = ReplayStateMeta.DomainEvent.class,
@@ -244,12 +312,14 @@ public abstract class Command implements DomainChangeRecord {
     /**
      * For a replayed command, what the outcome was.
      */
-    @Command.ReplayStateMeta
+    @ReplayStateMeta
     public abstract org.apache.isis.extensions.commandlog.applib.dom.ReplayState getReplayState();
     public abstract void setReplayState(org.apache.isis.extensions.commandlog.applib.dom.ReplayState replayState);
 
 
 
+    // REPLAY STATE FAILURE REASON
+
     @Property(
             domainEvent = ReplayStateFailureReason.DomainEvent.class,
             editing = Editing.DISABLED,
@@ -274,8 +344,8 @@ public abstract class Command implements DomainChangeRecord {
     public @interface ReplayStateFailureReason {
         int MAX_LENGTH = Integer.MAX_VALUE;
         int TYPICAL_LENGTH = 4000;
-
         int MULTI_LINE = 5;
+
         class DomainEvent extends PropertyDomainEvent<String> {}
     }
 
@@ -292,6 +362,7 @@ public abstract class Command implements DomainChangeRecord {
     }
 
 
+    // PARENT
 
     @Property(
             domainEvent = Parent.DomainEvent.class
@@ -302,27 +373,29 @@ public abstract class Command implements DomainChangeRecord {
     @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
     @Retention(RetentionPolicy.RUNTIME)
     public @interface Parent {
-        class DomainEvent extends PropertyDomainEvent<org.apache.isis.applib.services.command.Command> { }
+        class DomainEvent extends PropertyDomainEvent<Command> { }
     }
 
     @Parent
-    public abstract Command getParent();
-    public abstract void setParent(Command parent);
+    public abstract PublishedCommand getParent();
+    public abstract void setParent(PublishedCommand parent);
 
 
 
+    // TARGET
+
     @DomainChangeRecord.TargetMeta
     public abstract Bookmark getTarget();
     public abstract void setTarget(Bookmark target);
 
 
-
     @Domain.Exclude
     public String getTargetStr() {
         return Optional.ofNullable(getTarget()).map(Bookmark::toString).orElse(null);
     }
 
 
+    // TARGET MEMBER
 
     @DomainChangeRecord.TargetMember
     @Override
@@ -332,6 +405,8 @@ public abstract class Command implements DomainChangeRecord {
 
 
 
+    // LOCAL MEMBER
+
     @Property(
             domainEvent = LocalMember.DomainEvent.class,
             maxLength = LocalMember.MAX_LENGTH
@@ -363,6 +438,8 @@ public abstract class Command implements DomainChangeRecord {
 
 
 
+    // LOGICAL MEMBER IDENTIFIER
+
     @Property(
             domainEvent = LogicalMemberIdentifier.DomainEvent.class,
             editing = Editing.DISABLED,
@@ -395,6 +472,7 @@ public abstract class Command implements DomainChangeRecord {
 
 
 
+    // COMMAND DTO
 
     @Property(
             domainEvent = CommandDtoMeta.DomainEvent.class,
@@ -430,6 +508,7 @@ public abstract class Command implements DomainChangeRecord {
 
 
 
+    // STARTED AT
 
     @Property(
             domainEvent = StartedAt.DomainEvent.class,
@@ -460,6 +539,8 @@ public abstract class Command implements DomainChangeRecord {
 
 
 
+    // COMPLETED AT
+
     @Property(
             domainEvent = CompletedAt.DomainEvent.class,
             editing = Editing.DISABLED,
@@ -489,6 +570,8 @@ public abstract class Command implements DomainChangeRecord {
 
 
 
+    // DURATION
+
     @javax.validation.constraints.Digits(
             integer = Duration.DIGITS_INTEGER,
             fraction = Duration.DIGITS_FRACTION
@@ -530,8 +613,10 @@ public abstract class Command implements DomainChangeRecord {
 
 
 
+    // COMPLETE
+
     @Property(
-            domainEvent = IsComplete.DomainEvent.class,
+            domainEvent = Complete.DomainEvent.class,
             editing = Editing.DISABLED
     )
     @PropertyLayout(
@@ -539,17 +624,19 @@ public abstract class Command implements DomainChangeRecord {
     )
     @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
     @Retention(RetentionPolicy.RUNTIME)
-    public @interface IsComplete {
+    public @interface Complete {
         class DomainEvent extends PropertyDomainEvent<Boolean> { }
     }
 
-    @IsComplete
+    @Complete
     public boolean isComplete() {
         return getCompletedAt() != null;
     }
 
 
 
+    // RESULT SUMMARY
+
     @Property(
             domainEvent = ResultSummary.DomainEvent.class,
             editing = Editing.DISABLED,
@@ -591,8 +678,10 @@ public abstract class Command implements DomainChangeRecord {
 
 
 
+    // RESULT
+
     @Property(
-            domainEvent = ResultMeta.DomainEvent.class,
+            domainEvent = Result.DomainEvent.class,
             editing = Editing.DISABLED,
             optionality = Optionality.OPTIONAL
     )
@@ -608,16 +697,18 @@ public abstract class Command implements DomainChangeRecord {
     )
     @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
     @Retention(RetentionPolicy.RUNTIME)
-    public @interface ResultMeta {
+    public @interface Result {
         class DomainEvent extends PropertyDomainEvent<Bookmark> { }
     }
 
-    @ResultMeta
+    @Result
     public abstract Bookmark getResult();
     public abstract void setResult(Bookmark result);
 
 
 
+    // EXCEPTION
+
     @Property(
             domainEvent = ExceptionMeta.DomainEvent.class,
             editing = Editing.DISABLED,
@@ -662,6 +753,8 @@ public abstract class Command implements DomainChangeRecord {
 
 
 
+    // CAUSED EXCEPTION
+
     @Property(
             domainEvent = CausedException.DomainEvent.class,
             editing = Editing.DISABLED
@@ -682,6 +775,8 @@ public abstract class Command implements DomainChangeRecord {
 
 
 
+    // PRE VALUE
+
     @DomainChangeRecord.PreValue
     @Override
     public String getPreValue() {
@@ -689,6 +784,9 @@ public abstract class Command implements DomainChangeRecord {
     }
 
 
+
+    // POST VALUE
+
     @DomainChangeRecord.PostValue
     @Override
     public String getPostValue() {
@@ -696,6 +794,9 @@ public abstract class Command implements DomainChangeRecord {
     }
 
 
+
+    // PROGRAMMATIC
+
     @Programmatic
     public void saveAnalysis(final String analysis) {
         if (analysis == null) {
@@ -706,53 +807,59 @@ public abstract class Command implements DomainChangeRecord {
         }
     }
 
-    @Override
-    public String toString() {
-        return toFriendlyString();
-    }
-
-    String toFriendlyString() {
-        return ObjectContracts
-                .toString("interactionId", Command::getInteractionId)
-                .thenToString("username", Command::getUsername)
-                .thenToString("timestamp", Command::getTimestamp)
-                .thenToString("target", Command::getTarget)
-                .thenToString("logicalMemberIdentifier", Command::getLogicalMemberIdentifier)
-                .thenToStringOmitIfAbsent("startedAt", Command::getStartedAt)
-                .thenToStringOmitIfAbsent("completedAt", Command::getCompletedAt)
-                .toString(this);
-    }
-
+    @Programmatic
     public CommandOutcomeHandler outcomeHandler() {
         return new CommandOutcomeHandler() {
             @Override
             public Timestamp getStartedAt() {
-                return Command.this.getStartedAt();
+                return PublishedCommand.this.getStartedAt();
             }
 
             @Override
             public void setStartedAt(final Timestamp startedAt) {
-                Command.this.setStartedAt(startedAt);
+                PublishedCommand.this.setStartedAt(startedAt);
             }
 
             @Override
             public void setCompletedAt(final Timestamp completedAt) {
-                Command.this.setCompletedAt(completedAt);
+                PublishedCommand.this.setCompletedAt(completedAt);
             }
 
             @Override
-            public void setResult(final Result<Bookmark> resultBookmark) {
-                Command.this.setResult(resultBookmark.getValue().orElse(null));
-                Command.this.setException(resultBookmark.getFailure().orElse(null));
+            public void setResult(final org.apache.isis.commons.functional.Result resultBookmark) {
+                PublishedCommand.this.setResult(resultBookmark.getValue().orElse(null));
+                PublishedCommand.this.setException(resultBookmark.getFailure().orElse(null));
             }
         };
     }
 
-    @Service
+
+
+    // TO STRING etc
+
+    @Override
+    public String toString() {
+        return toFriendlyString();
+    }
+
+    String toFriendlyString() {
+        return ObjectContracts
+                .toString("interactionId", PublishedCommand::getInteractionId)
+                .thenToString("username", PublishedCommand::getUsername)
+                .thenToString("timestamp", PublishedCommand::getTimestamp)
+                .thenToString("target", PublishedCommand::getTarget)
+                .thenToString("logicalMemberIdentifier", PublishedCommand::getLogicalMemberIdentifier)
+                .thenToStringOmitIfAbsent("startedAt", PublishedCommand::getStartedAt)
+                .thenToStringOmitIfAbsent("completedAt", PublishedCommand::getCompletedAt)
+                .toString(this);
+    }
+
+
+    @Component
     @javax.annotation.Priority(PriorityPrecedence.LATE - 10)
-    public static class TableColumnOrderDefault extends TableColumnOrderForCollectionTypeAbstract<Command> {
+    public static class TableColumnOrderDefault extends TableColumnOrderForCollectionTypeAbstract<PublishedCommand> {
 
-        public TableColumnOrderDefault() { super(Command.class); }
+        public TableColumnOrderDefault() { super(PublishedCommand.class); }
 
         @Override
         protected List<String> orderParented(final Object parent, final String collectionId, final List<String> propertyIds) {
@@ -776,6 +883,5 @@ public abstract class Command implements DomainChangeRecord {
             );
         }
     }
-
 }
 
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.layout.fallback.xml b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommand.layout.fallback.xml
similarity index 100%
rename from extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.layout.fallback.xml
rename to extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommand.layout.fallback.xml
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.png b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommand.png
similarity index 100%
rename from extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.png
rename to extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommand.png
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandModelRepository.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommandRepository.java
similarity index 64%
rename from extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandModelRepository.java
rename to extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommandRepository.java
index cc65c64..4dacbd4 100644
--- a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/CommandModelRepository.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/PublishedCommandRepository.java
@@ -27,55 +27,55 @@ import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.CommandsDto;
 
-public interface CommandModelRepository<C extends CommandModel> {
+public interface PublishedCommandRepository {
 
-    Optional<C> findByInteractionId(UUID interactionId);
+    Optional<PublishedCommand> findByInteractionId(UUID interactionId);
 
-    List<C> findByParent(CommandModel parent);
+    List<PublishedCommand> findByParent(PublishedCommand parent);
 
-    List<C> findByFromAndTo(LocalDate from, LocalDate to);
+    List<PublishedCommand> findByFromAndTo(LocalDate from, LocalDate to);
 
-    List<C> findCurrent();
+    List<PublishedCommand> findCurrent();
 
-    List<C> findCompleted();
+    List<PublishedCommand> findCompleted();
 
-    List<C> findByTargetAndFromAndTo(Bookmark target, LocalDate from, LocalDate to);
+    List<PublishedCommand> findByTargetAndFromAndTo(Bookmark target, LocalDate from, LocalDate to);
 
-    List<C> findRecentByUsername(String username);
+    List<PublishedCommand> findRecentByUsername(String username);
 
-    List<C> findRecentByTarget(Bookmark target);
+    List<PublishedCommand> findRecentByTarget(Bookmark target);
 
     /**
      * Intended to support the replay of commands on a secondary instance of
      * the application.
      *
-     * This finder returns all (completed) {@link CommandModel}s started after
+     * This finder returns all (completed) {@link PublishedCommand}s started after
      * the command with the specified interactionId.  The number of commands
      * returned can be limited so that they can be applied in batches.
      *
      * If the provided interactionId is null, then only a single
-     * {@link CommandModel command} is returned.  This is intended to support
+     * {@link PublishedCommand command} is returned.  This is intended to support
      * the case when the secondary does not yet have any
-     * {@link CommandModel command}s replicated.  In practice this is unlikely;
+     * {@link PublishedCommand command}s replicated.  In practice this is unlikely;
      * typically we expect that the secondary will be set up to run against a
      * copy of the primary instance's DB (restored from a backup), in which
-     * case there will already be a {@link CommandModel command} representing the
+     * case there will already be a {@link PublishedCommand command} representing the
      * current high water mark on the secondary system.
      *
      * If the interactionId is not null but the corresponding
-     * {@link CommandModel command} is not found, then <tt>null</tt> is returned.
+     * {@link PublishedCommand command} is not found, then <tt>null</tt> is returned.
      * In the replay scenario the caller will probably interpret this as an
      * error because it means that the high water mark on the secondary is
-     * inaccurate, referring to a non-existent {@link CommandModel command} on
+     * inaccurate, referring to a non-existent {@link PublishedCommand command} on
      * the primary.
      *
-     * @param interactionId - the identifier of the {@link CommandModel command} being
+     * @param interactionId - the identifier of the {@link PublishedCommand command} being
      *                   the replay hwm (using {@link #findMostRecentReplayed()} on the
      *                   secondary), or null if no HWM was found there.
      * @param batchSize - to restrict the number returned (so that replay
      *                   commands can be batched).
      */
-    List<C> findSince(UUID interactionId, Integer batchSize);
+    List<PublishedCommand> findSince(UUID interactionId, Integer batchSize);
 
     /**
      * The most recent replayed command previously replicated from primary to
@@ -86,7 +86,7 @@ public interface CommandModelRepository<C extends CommandModel> {
      * (after restored the prod DB to secondary).
      * </p>
      */
-    Optional<C> findMostRecentReplayed();
+    Optional<PublishedCommand> findMostRecentReplayed();
 
     /**
      * The most recent completed command, as queried on the
@@ -99,20 +99,19 @@ public interface CommandModelRepository<C extends CommandModel> {
      *     secondary.
      * </p>
      */
-    Optional<C> findMostRecentCompleted();
+    Optional<PublishedCommand> findMostRecentCompleted();
 
-    List<C> findNotYetReplayed();
+    List<PublishedCommand> findNotYetReplayed();
 
-    List<C> findReplayedOnSecondary();
+    List<PublishedCommand> findReplayedOnSecondary();
 
-    C saveForReplay(CommandDto dto);
+    PublishedCommand saveForReplay(CommandDto dto);
 
-    List<C> saveForReplay(CommandsDto commandsDto);
+    List<PublishedCommand> saveForReplay(CommandsDto commandsDto);
 
-    void persist(C commandJdo);
+    void persist(PublishedCommand commandJdo);
 
     void truncateLog();
 
 
-
 }
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_childCommands.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_childCommands.java
similarity index 58%
rename from extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_childCommands.java
rename to extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_childCommands.java
index d55341c..eedeea3 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_childCommands.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_childCommands.java
@@ -16,38 +16,40 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.commandlog.jdo.entities;
+package org.apache.isis.extensions.commandlog.applib.dom.mixins;
 
 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.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
+import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
 
 import lombok.RequiredArgsConstructor;
 
 
 @Collection(
-    domainEvent = CommandJdo_childCommands.CollectionDomainEvent.class
+    domainEvent = PublishedCommand_childCommands.DomainEvent.class
 )
 @CollectionLayout(
     defaultView = "table",
     sequence = "100.100"
 )
 @RequiredArgsConstructor
-public class CommandJdo_childCommands {
+public class PublishedCommand_childCommands {
 
-    public static class CollectionDomainEvent
-            extends IsisModuleExtCommandLogJdo.CollectionDomainEvent<CommandJdo_childCommands, CommandModel> { }
+    public static class DomainEvent
+            extends IsisModuleExtCommandLogApplib.CollectionDomainEvent<PublishedCommand_childCommands, PublishedCommand> { }
 
-    private final CommandJdo commandJdo;
+    private final PublishedCommand publishedCommand;
 
-    public List<CommandJdo> coll() {
-        return commandJdoRepository.findByParent(commandJdo);
+    public List<PublishedCommand> coll() {
+        return repository.findByParent(publishedCommand);
     }
 
-    @javax.inject.Inject
-    private CommandJdoRepository commandJdoRepository;
+    @Inject PublishedCommandRepository repository;
 
 }
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_openResultObject.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_openResultObject.java
similarity index 68%
rename from extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_openResultObject.java
rename to extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_openResultObject.java
index 59a1687..63528df 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_openResultObject.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_openResultObject.java
@@ -16,43 +16,45 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.commandlog.jdo.entities;
+package org.apache.isis.extensions.commandlog.applib.dom.mixins;
 
 import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 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.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
+import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
 
 import lombok.RequiredArgsConstructor;
 import lombok.val;
 
 @Action(
     semantics = SemanticsOf.SAFE,
-    domainEvent = CommandJdo_openResultObject.ActionDomainEvent.class
+    domainEvent = PublishedCommand_openResultObject.DomainEvent.class
 )
 @ActionLayout(named = "Open", associateWith = "result", sequence="1")
 @RequiredArgsConstructor
-public class CommandJdo_openResultObject {
+public class PublishedCommand_openResultObject {
 
-    public static class ActionDomainEvent
-            extends IsisModuleExtCommandLogJdo.ActionDomainEvent<CommandJdo_openResultObject> { }
+    public static class DomainEvent
+            extends IsisModuleExtCommandLogApplib.ActionDomainEvent<PublishedCommand_openResultObject> { }
 
-    private final CommandJdo commandJdo;
+    private final PublishedCommand publishedCommand;
 
     public Object act() {
-        val targetBookmark = bookmarkService.lookup(commandJdo.getResult()).orElse(null);
+        val targetBookmark = bookmarkService.lookup(publishedCommand.getResult()).orElse(null);
         if(targetBookmark == null) {
             messageService.warnUser("Object not found - has it since been deleted?");
             return null;
         }
         return targetBookmark;
     }
-    public boolean hideAct() {
-        return commandJdo.getResult() == null;
+    @MemberSupport public boolean hideAct() {
+        return publishedCommand.getResult() == null;
     }
 
     @Inject BookmarkService bookmarkService;
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_openTargetObject.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_openTargetObject.java
similarity index 62%
rename from extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_openTargetObject.java
rename to extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_openTargetObject.java
index 3fb718d..3181a07 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_openTargetObject.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_openTargetObject.java
@@ -16,43 +16,51 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.commandlog.jdo.entities;
+package org.apache.isis.extensions.commandlog.applib.dom.mixins;
 
 import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Action;
 import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.MemberSupport;
 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.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
+import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
 
 import lombok.RequiredArgsConstructor;
 import lombok.val;
 
 @Action(
-    semantics = SemanticsOf.SAFE,
-    domainEvent = CommandJdo_openTargetObject.ActionDomainEvent.class
+        semantics = SemanticsOf.SAFE,
+        domainEvent = PublishedCommand_openTargetObject.ActionDomainEvent.class
+)
+@ActionLayout(
+        associateWith = "target",
+        named = "Open",
+        sequence = "1"
 )
-@ActionLayout(named = "Open", associateWith = "target", sequence="1")
 @RequiredArgsConstructor
-public class CommandJdo_openTargetObject {
+public class PublishedCommand_openTargetObject {
 
     public static class ActionDomainEvent
-            extends IsisModuleExtCommandLogJdo.ActionDomainEvent<CommandJdo_openTargetObject> { }
+            extends IsisModuleExtCommandLogApplib.ActionDomainEvent<PublishedCommand_openTargetObject> {
+    }
 
-    private final CommandJdo commandJdo;
+    private final PublishedCommand publishedCommand;
 
     public Object act() {
-        val targetBookmark = bookmarkService.lookup(commandJdo.getTarget()).orElse(null);
-        if(targetBookmark == null) {
+        val targetBookmark = bookmarkService.lookup(publishedCommand.getTarget()).orElse(null);
+        if (targetBookmark == null) {
             messageService.warnUser("Object not found - has it since been deleted?");
             return null;
         }
         return targetBookmark;
     }
-    public boolean hideAct() {
-        return commandJdo.getTarget() == null;
+
+    @MemberSupport public boolean hideAct() {
+        return publishedCommand.getTarget() == null;
     }
 
     @Inject BookmarkService bookmarkService;
diff --git a/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_retry.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_retry.java
new file mode 100644
index 0000000..6b21582
--- /dev/null
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_retry.java
@@ -0,0 +1,59 @@
+/*
+ *  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.applib.dom.mixins;
+
+import org.apache.isis.applib.annotation.Action;
+import org.apache.isis.applib.annotation.ActionLayout;
+import org.apache.isis.applib.annotation.Publishing;
+import org.apache.isis.applib.annotation.SemanticsOf;
+import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
+import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
+
+import lombok.RequiredArgsConstructor;
+
+@Action(
+        commandPublishing = Publishing.DISABLED,
+        domainEvent = PublishedCommand_retry.DomainEvent.class,
+        semantics = SemanticsOf.NON_IDEMPOTENT_ARE_YOU_SURE
+)
+@ActionLayout(
+        associateWith = "executeIn",
+        sequence = "1"
+)
+@RequiredArgsConstructor
+public class PublishedCommand_retry {
+
+    public static class DomainEvent extends IsisModuleExtCommandLogApplib.ActionDomainEvent<PublishedCommand_retry> {
+    }
+
+    private final PublishedCommand publishedCommand;
+
+    public PublishedCommand act() {
+
+        publishedCommand.setReplayState(ReplayState.PENDING);
+        publishedCommand.setResult(null);
+        publishedCommand.setException((Exception) null);
+        publishedCommand.setStartedAt(null);
+        publishedCommand.setCompletedAt(null);
+
+        return publishedCommand;
+    }
+
+}
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_siblingCommands.java b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_siblingCommands.java
similarity index 58%
rename from extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_siblingCommands.java
rename to extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_siblingCommands.java
index 22c6200..58cf1e6 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_siblingCommands.java
+++ b/extensions/core/command-log/applib/src/main/java/org/apache/isis/extensions/commandlog/applib/dom/mixins/PublishedCommand_siblingCommands.java
@@ -16,7 +16,7 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-package org.apache.isis.extensions.commandlog.jdo.entities;
+package org.apache.isis.extensions.commandlog.applib.dom.mixins;
 
 import java.util.Collections;
 import java.util.List;
@@ -25,36 +25,39 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
-import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
+import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
 
 import lombok.RequiredArgsConstructor;
+import lombok.val;
 
 @Collection(
-    domainEvent = CommandJdo_siblingCommands.CollectionDomainEvent.class
+    domainEvent = PublishedCommand_siblingCommands.CollectionDomainEvent.class
 )
 @CollectionLayout(
     defaultView = "table",
     sequence = "100.110"
 )
 @RequiredArgsConstructor
-public class CommandJdo_siblingCommands {
+public class PublishedCommand_siblingCommands {
 
     public static class CollectionDomainEvent
-            extends IsisModuleExtCommandLogJdo.CollectionDomainEvent<CommandJdo_siblingCommands, CommandJdo> { }
+            extends IsisModuleExtCommandLogApplib.CollectionDomainEvent<PublishedCommand_siblingCommands, PublishedCommand> { }
 
-    private final CommandJdo commandJdo;
+    private final PublishedCommand publishedCommand;
 
-    public List<CommandJdo> coll() {
-        final CommandJdo parentJdo = commandJdo.getParent();
-        if(parentJdo == null) {
+    public List<PublishedCommand> coll() {
+        val parentCommand = publishedCommand.getParent();
+        if(parentCommand == null) {
             return Collections.emptyList();
         }
-        final List<CommandJdo> siblingCommands = commandJdoRepository.findByParent(parentJdo);
-        siblingCommands.remove(commandJdo);
+        val siblingCommands = repository.findByParent(parentCommand);
+        siblingCommands.remove(publishedCommand);
         return siblingCommands;
     }
 
 
-    @Inject CommandJdoRepository commandJdoRepository;
+    @Inject PublishedCommandRepository repository;
 
 }
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/CommandSubscriberForJdo.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/CommandSubscriberForJdo.java
index fffc033..f3ab1d2 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/CommandSubscriberForJdo.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/CommandSubscriberForJdo.java
@@ -21,14 +21,14 @@ package org.apache.isis.extensions.commandlog.jdo;
 import javax.inject.Inject;
 import javax.inject.Named;
 
-import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Service;
 
+import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.publishing.spi.CommandSubscriber;
 import org.apache.isis.applib.util.JaxbUtil;
-import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo;
+import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo;
 import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
 
 import lombok.RequiredArgsConstructor;
@@ -39,7 +39,7 @@ import lombok.extern.log4j.Log4j2;
  * @since 2.0 {@index}
  */
 @Service
-@Named("isis.ext.commandLog.CommandCompletionHook")
+@Named("isis.ext.commandLog.CommandSubscriberForJdo")
 @javax.annotation.Priority(PriorityPrecedence.MIDPOINT) // after JdoPersistenceLifecycleService
 @Qualifier("Jdo")
 @Log4j2
@@ -69,7 +69,7 @@ public class CommandSubscriberForJdo implements CommandSubscriber {
                 log.debug("proposed: \n{}", commandDtoXml);
             }
         } else {
-            val commandJdo = new CommandJdo(command);
+            val commandJdo = new PublishedCommandForJdo(command);
             val parent = command.getParent();
             val parentJdo =
                 parent != null
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
index cad004a..9b2911e 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/IsisModuleExtCommandLogJdo.java
@@ -22,11 +22,10 @@ import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 
-import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo;
+import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo;
 import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
 import org.apache.isis.extensions.commandlog.jdo.ui.CommandServiceMenu;
 import org.apache.isis.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
 import org.apache.isis.testing.fixtures.applib.fixturescripts.FixtureScript;
 import org.apache.isis.testing.fixtures.applib.teardown.jdo.TeardownFixtureJdoAbstract;
 
@@ -40,10 +39,10 @@ import org.apache.isis.testing.fixtures.applib.teardown.jdo.TeardownFixtureJdoAb
         , CommandServiceMenu.class
 
         // @Service's
-        , CommandJdo.TableColumnOrderDefault.class
+        , PublishedCommandForJdo.TableColumnOrderDefault.class
 
         // entities
-        , CommandJdo.class
+        , PublishedCommandForJdo.class
 })
 @ComponentScan(
         basePackageClasses= {
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.java
deleted file mode 100644
index 3042c5d..0000000
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo.java
+++ /dev/null
@@ -1,647 +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.extensions.commandlog.jdo.entities;
-
-import java.math.BigDecimal;
-import java.sql.Timestamp;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.UUID;
-import java.util.function.Consumer;
-
-import javax.jdo.annotations.IdentityType;
-
-import org.springframework.context.event.EventListener;
-import org.springframework.stereotype.Service;
-
-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.MemberSupport;
-import org.apache.isis.applib.annotation.PriorityPrecedence;
-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.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
-import org.apache.isis.applib.mixins.system.DomainChangeRecord;
-import org.apache.isis.applib.services.bookmark.Bookmark;
-import org.apache.isis.applib.services.command.Command;
-import org.apache.isis.applib.services.command.CommandOutcomeHandler;
-import org.apache.isis.applib.services.commanddto.conmap.UserDataKeys;
-import org.apache.isis.applib.services.tablecol.TableColumnOrderForCollectionTypeAbstract;
-import org.apache.isis.applib.types.MemberIdentifierType;
-import org.apache.isis.applib.util.TitleBuffer;
-import org.apache.isis.commons.functional.Result;
-import org.apache.isis.commons.internal.base._Strings;
-import org.apache.isis.commons.internal.exceptions._Exceptions;
-import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
-import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
-import org.apache.isis.extensions.commandlog.applib.util.BigDecimalUtils;
-import org.apache.isis.extensions.commandlog.applib.util.StringUtils;
-import org.apache.isis.schema.cmd.v2.CommandDto;
-import org.apache.isis.schema.cmd.v2.MapDto;
-
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.Setter;
-import lombok.val;
-
-/**
- * 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 = "isisExtensionsCommandLog",
-        table = "Command")
-@javax.jdo.annotations.Queries( {
-    @javax.jdo.annotations.Query(
-            name="findByInteractionIdStr",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE interactionIdStr == :interactionIdStr "),
-    @javax.jdo.annotations.Query(
-            name="findByParent",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE parent == :parent "),
-    @javax.jdo.annotations.Query(
-            name="findCurrent",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE completedAt == null "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findCompleted",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE completedAt != null "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findRecentByTarget",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE target == :target "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,30"),
-    @javax.jdo.annotations.Query(
-            name="findByTargetAndTimestampBetween",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE target == :target "
-                    + "&& timestamp >= :from "
-                    + "&& timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTargetAndTimestampAfter",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE target == :target "
-                    + "&& timestamp >= :from "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTargetAndTimestampBefore",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE target == :target "
-                    + "&& timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTarget",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE target == :target "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTimestampBetween",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE timestamp >= :from "
-                    + "&&    timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTimestampAfter",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE timestamp >= :from "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findByTimestampBefore",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE timestamp <= :to "
-                    + "ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="find",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " ORDER BY this.timestamp DESC"),
-    @javax.jdo.annotations.Query(
-            name="findRecentByUsername",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE username == :username "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,30"),
-    @javax.jdo.annotations.Query(
-            name="findFirst",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE startedAt   != null "
-                    + "   && completedAt != null "
-                    + "ORDER BY this.timestamp ASC "
-                    + "RANGE 0,2"),
-        // 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="findSince",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE timestamp > :timestamp "
-                    + "   && startedAt != null "
-                    + "   && completedAt != null "
-                    + "ORDER BY this.timestamp ASC"),
-    // most recent (replayed) command previously replicated from primary to
-    // secondary.  This should always exist except for the very first times
-    // (after restored the prod DB to secondary).
-    @javax.jdo.annotations.Query(
-            name="findMostRecentReplayed",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE (replayState == 'OK' || replayState == 'FAILED') "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,2"), // 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
-    // the most recent completed command, as queried on the
-    // secondary, corresponding to the last command run on primary before the
-    // production database was restored to the secondary
-    @javax.jdo.annotations.Query(
-            name="findMostRecentCompleted",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE startedAt   != null "
-                    + "   && completedAt != null "
-                    + "ORDER BY this.timestamp DESC "
-                    + "RANGE 0,2"),
-        // 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="findNotYetReplayed",
-            value="SELECT "
-                    + "FROM " + CommandJdo.FQCN
-                    + " WHERE replayState == 'PENDING' "
-                    + "ORDER BY this.timestamp ASC "
-                    + "RANGE 0,10"),    // same as batch size
-//        @javax.jdo.annotations.Query(
-//                name="findReplayableInErrorMostRecent",
-//                value="SELECT "
-//                        + "FROM " + CommandJdo.FQCN
-//                        + " WHERE replayState == 'FAILED' "
-//                        + "ORDER BY this.timestamp DESC "
-//                        + "RANGE 0,2"),
-//    @javax.jdo.annotations.Query(
-//            name="findReplayableMostRecentStarted",
-//            value="SELECT "
-//                    + "FROM " + CommandJdo.FQCN
-//                    + " WHERE replayState = 'PENDING' "
-//                    + "ORDER BY this.timestamp DESC "
-//                    + "RANGE 0,20"),
-})
-@javax.jdo.annotations.Indices({
-        @javax.jdo.annotations.Index(name = "CommandJdo__startedAt__timestamp__IDX", members = { "startedAt", "timestamp" }),
-        @javax.jdo.annotations.Index(name = "CommandJdo__timestamp__IDX", members = { "timestamp" }),
-//        @javax.jdo.annotations.Index(name = "CommandJdo__replayState__timestamp__startedAt_IDX", members = { "replayState", "timestamp", "startedAt"}),
-//        @javax.jdo.annotations.Index(name = "CommandJdo__replayState__startedAt__completedAt_IDX", members = {"startedAt", "replayState", "completedAt"}),
-})
-@DomainObject(
-        logicalTypeName = CommandJdo.LOGICAL_TYPE_NAME,
-        editing = Editing.DISABLED
-)
-@DomainObjectLayout(
-        named = "Command",
-        titleUiEvent = CommandModel.TitleUiEvent.class,
-        iconUiEvent = CommandModel.IconUiEvent.class,
-        cssClassUiEvent = CommandModel.CssClassUiEvent.class,
-        layoutUiEvent = CommandModel.LayoutUiEvent.class
-)
-//@Log4j2
-@NoArgsConstructor
-public class CommandJdo
-implements
-    CommandModel,
-    DomainChangeRecord {
-
-    public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogJdo.NAMESPACE + ".Command";
-
-    protected final static String FQCN = "org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo";
-
-    /**
-     * Intended for use on primary system.
-     *
-     * @param command
-     */
-    public CommandJdo(final Command command) {
-
-        setInteractionIdStr(command.getInteractionId().toString());
-        setUsername(command.getUsername());
-        setTimestamp(command.getTimestamp());
-
-        setCommandDto(command.getCommandDto());
-        setTarget(command.getTarget());
-        setLogicalMemberIdentifier(command.getLogicalMemberIdentifier());
-
-        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) {
-
-        setInteractionIdStr(commandDto.getInteractionId());
-        setUsername(commandDto.getUser());
-        setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(commandDto.getTimestamp()));
-
-        setCommandDto(commandDto);
-        setTarget(Bookmark.forOidDto(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()));
-
-        copyOver(commandDto, UserDataKeys.RESULT, value -> this.setResult(Bookmark.parse(value).orElse(null)));
-        copyOver(commandDto, UserDataKeys.EXCEPTION, this::setException);
-
-        setReplayState(replayState);
-    }
-
-    static void copyOver(
-            final CommandDto commandDto,
-            final String key, final Consumer<String> consume) {
-        commandDto.getUserData().getEntry()
-                .stream()
-                .filter(x -> Objects.equals(x.getKey(), key))
-                .map(MapDto.Entry::getValue)
-                .filter(Objects::nonNull)
-                .filter(x -> x.length() > 0)
-                .findFirst()
-                .ifPresent(consume);
-    }
-
-    @Service
-    public static class TitleProvider {
-
-        @EventListener(TitleUiEvent.class)
-        public void on(final TitleUiEvent ev) {
-            if(!Objects.equals(ev.getTitle(), "Command Jdo") || ev.getTranslatableTitle() != null) {
-                return;
-            }
-            ev.setTitle(title((CommandJdo)ev.getSource()));
-        }
-
-        private static String title(final CommandJdo source) {
-            // nb: not thread-safe
-            // formats defined in https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html
-            val format = new SimpleDateFormat("yyyy-MM-dd HH:mm");
-
-            val buf = new TitleBuffer();
-            buf.append(format.format(source.getTimestamp()));
-            buf.append(" ").append(source.getLogicalMemberIdentifier());
-            return buf.toString();
-        }
-    }
-
-
-    public static class InteractionIdDomainEvent extends PropertyDomainEvent<String> { }
-    /**
-     * Implementation note: persisted as a string rather than a UUID as fails
-     * to persist if using h2 (perhaps would need to be mapped differently).
-     * @see <a href="https://www.datanucleus.org/products/accessplatform/jdo/mapping.html#_other_types">www.datanucleus.org</a>
-     */
-    @javax.jdo.annotations.PrimaryKey
-    @javax.jdo.annotations.Persistent
-    @javax.jdo.annotations.Column(allowsNull="false", name = "interactionId", length = 36)
-    @Property(domainEvent = InteractionIdDomainEvent.class)
-    @PropertyLayout(named = "Interaction Id")
-    @Getter @Setter
-    private String interactionIdStr;
-    @Override
-    @Programmatic
-    public UUID getInteractionId() {return UUID.fromString(getInteractionIdStr());}
-
-
-    public static class UsernameDomainEvent extends PropertyDomainEvent<String> { }
-    @javax.jdo.annotations.Column(allowsNull="false", length = 50)
-    @Property(domainEvent = UsernameDomainEvent.class)
-    @Getter @Setter
-    private String username;
-
-
-    public static class TimestampDomainEvent extends PropertyDomainEvent<Timestamp> { }
-    @javax.jdo.annotations.Persistent
-    @javax.jdo.annotations.Column(allowsNull="false")
-    @Property(domainEvent = TimestampDomainEvent.class)
-    @Getter @Setter
-    private Timestamp timestamp;
-
-
-
-    @Override
-    public ChangeType getType() {
-        return ChangeType.COMMAND;
-    }
-
-
-    public static class ReplayStateDomainEvent extends PropertyDomainEvent<ReplayState> { }
-    /**
-     * For a replayed command, what the outcome was.
-     */
-    @javax.jdo.annotations.Column(allowsNull="true", length=10)
-    @Property(domainEvent = ReplayStateDomainEvent.class)
-    @Getter @Setter
-    private ReplayState replayState;
-
-
-    public static class ReplayStateFailureReasonDomainEvent extends PropertyDomainEvent<ReplayState> { }
-    /**
-     * For a {@link ReplayState#FAILED failed} replayed command, what the reason was for the failure.
-     */
-    @javax.jdo.annotations.Column(allowsNull="true", length=255)
-    @Property(domainEvent = ReplayStateFailureReasonDomainEvent.class)
-    @PropertyLayout(hidden = Where.ALL_TABLES, multiLine = 5)
-    @Getter @Setter
-    private String replayStateFailureReason;
-    @MemberSupport public boolean hideReplayStateFailureReason() {
-        return getReplayState() == null || !getReplayState().isFailed();
-    }
-
-
-    public static class ParentDomainEvent extends PropertyDomainEvent<Command> { }
-    @javax.jdo.annotations.Persistent
-    @javax.jdo.annotations.Column(name="parentId", allowsNull="true")
-    @Property(domainEvent = ParentDomainEvent.class)
-    @PropertyLayout(hidden = Where.ALL_TABLES)
-    @Getter @Setter
-    private CommandJdo parent;
-
-
-    public static class TargetDomainEvent extends PropertyDomainEvent<String> { }
-    @javax.jdo.annotations.Persistent
-    @javax.jdo.annotations.Column(allowsNull="true", length = 2000, name="target")
-    @Property(domainEvent = TargetDomainEvent.class)
-    @PropertyLayout(named = "Object")
-    @Getter @Setter
-    private Bookmark target;
-
-    public String getTargetStr() {
-        return Optional.ofNullable(getTarget()).map(Bookmark::toString).orElse(null);
-    }
-
-    @Override
-    public String getTargetMember() {
-        return getCommandDto().getMember().getLogicalMemberIdentifier();
-    }
-
-    @Property(domainEvent = TargetDomainEvent.class)
-    @PropertyLayout(named = "Member")
-    public String getLocalMember() {
-        val targetMember = getTargetMember();
-        return targetMember.substring(targetMember.indexOf("#") + 1);
-    }
-
-    public static class LogicalMemberIdentifierDomainEvent extends PropertyDomainEvent<String> { }
-    @Property(domainEvent = LogicalMemberIdentifierDomainEvent.class)
-    @PropertyLayout(hidden = Where.ALL_TABLES)
-    @javax.jdo.annotations.Column(allowsNull="false", length = MemberIdentifierType.Meta.MAX_LEN)
-    @Getter @Setter
-    private String logicalMemberIdentifier;
-
-
-    public static class CommandDtoDomainEvent extends PropertyDomainEvent<CommandDto> { }
-    @javax.jdo.annotations.Persistent
-    @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB")
-    @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")
-    @Property(domainEvent = StartedAtDomainEvent.class)
-    @Getter @Setter
-    private Timestamp startedAt;
-
-
-    public static class CompletedAtDomainEvent extends PropertyDomainEvent<Timestamp> { }
-    @javax.jdo.annotations.Persistent
-    @javax.jdo.annotations.Column(allowsNull="true")
-    @Property(domainEvent = CompletedAtDomainEvent.class)
-    @Getter @Setter
-    private Timestamp completedAt;
-
-
-    public static class DurationDomainEvent extends PropertyDomainEvent<BigDecimal> { }
-    /**
-     * The number of seconds (to 3 decimal places) that this interaction lasted.
-     *
-     * <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() {
-        return BigDecimalUtils.durationBetween(getStartedAt(), getCompletedAt());
-    }
-
-
-    public static class IsCompleteDomainEvent extends PropertyDomainEvent<Boolean> { }
-    @javax.jdo.annotations.NotPersistent
-    @Property(domainEvent = IsCompleteDomainEvent.class)
-    @PropertyLayout(hidden = Where.OBJECT_FORMS)
-    public boolean isComplete() {
-        return getCompletedAt() != null;
-    }
-
-
-    public static class ResultSummaryDomainEvent extends PropertyDomainEvent<String> { }
-    @javax.jdo.annotations.NotPersistent
-    @Property(domainEvent = ResultSummaryDomainEvent.class)
-    @PropertyLayout(hidden = Where.OBJECT_FORMS, named = "Result")
-    public String getResultSummary() {
-        if(getCompletedAt() == null) {
-            return "";
-        }
-        if(!_Strings.isNullOrEmpty(getException())) {
-            return "EXCEPTION";
-        }
-        if(getResult() != null) {
-            return "OK";
-        } else {
-            return "OK (VOID)";
-        }
-    }
-
-
-    public static class ResultDomainEvent extends PropertyDomainEvent<String> { }
-    @javax.jdo.annotations.Persistent
-    @javax.jdo.annotations.Column(allowsNull="true", length = 2000, name="result")
-    @Property(domainEvent = ResultDomainEvent.class)
-    @PropertyLayout(hidden = Where.ALL_TABLES, named = "Result Bookmark")
-    @Getter @Setter
-    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")
-    @Property(domainEvent = ExceptionDomainEvent.class)
-    @PropertyLayout(hidden = Where.ALL_TABLES, multiLine = 5, named = "Exception (if any)")
-    @Getter
-    private String exception;
-    public void setException(final String exception) {
-        this.exception = exception;
-    }
-    public void setException(final Throwable exception) {
-        setException(_Exceptions.asStacktrace(exception));
-    }
-
-    public static class IsCausedExceptionDomainEvent extends PropertyDomainEvent<Boolean> { }
-    @javax.jdo.annotations.NotPersistent
-    @Property(domainEvent = IsCausedExceptionDomainEvent.class)
-    @PropertyLayout(hidden = Where.OBJECT_FORMS)
-    public boolean isCausedException() {
-        return getException() != null;
-    }
-
-
-    @Override
-    public String getPreValue() {
-        return null;
-    }
-
-    @Override
-    public String getPostValue() {
-        return null;
-    }
-
-
-    @Override
-    public void saveAnalysis(final String analysis) {
-        if (analysis == null) {
-            setReplayState(ReplayState.OK);
-        } else {
-            setReplayState(ReplayState.FAILED);
-            setReplayStateFailureReason(StringUtils.trimmed(analysis, 255));
-        }
-
-    }
-
-    @Override
-    public String toString() {
-        return toFriendlyString();
-    }
-
-    @Override
-    public CommandOutcomeHandler outcomeHandler() {
-        return new CommandOutcomeHandler() {
-            @Override
-            public Timestamp getStartedAt() {
-                return CommandJdo.this.getStartedAt();
-            }
-
-            @Override
-            public void setStartedAt(final Timestamp startedAt) {
-                CommandJdo.this.setStartedAt(startedAt);
-            }
-
-            @Override
-            public void setCompletedAt(final Timestamp completedAt) {
-                CommandJdo.this.setCompletedAt(completedAt);
-            }
-
-            @Override
-            public void setResult(final Result<Bookmark> resultBookmark) {
-                CommandJdo.this.setResult(resultBookmark.getValue().orElse(null));
-                CommandJdo.this.setException(resultBookmark.getFailure().orElse(null));
-            }
-
-        };
-    }
-
-    @Service
-    @javax.annotation.Priority(PriorityPrecedence.LATE - 10) // before the framework's own default.
-    public static class TableColumnOrderDefault extends TableColumnOrderForCollectionTypeAbstract<CommandJdo> {
-
-        public TableColumnOrderDefault() { super(CommandJdo.class); }
-
-        @Override
-        protected List<String> orderParented(final Object parent, final String collectionId, final List<String> propertyIds) {
-            return ordered(propertyIds);
-        }
-
-        @Override
-        protected List<String> orderStandalone(final List<String> propertyIds) {
-            return ordered(propertyIds);
-        }
-
-        private List<String> ordered(final List<String> propertyIds) {
-            return Arrays.asList(
-                "timestamp", "target", "targetMember", "username", "complete", "resultSummary", "interactionIdStr"
-            );
-        }
-    }
-}
-
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdoRepository.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdoRepository.java
index ae47780..3b1e60c 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdoRepository.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdoRepository.java
@@ -45,8 +45,7 @@ import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.iactn.InteractionProvider;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModelRepository;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
 import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
 import org.apache.isis.persistence.jdo.applib.services.JdoSupportService;
 import org.apache.isis.schema.cmd.v2.CommandDto;
@@ -60,7 +59,7 @@ import lombok.val;
 
 /**
  * Provides supporting functionality for querying and persisting
- * {@link CommandJdo command} entities.
+ * {@link PublishedCommandForJdo command} entities.
  */
 @Service
 @Named("isis.ext.commandLog.CommandJdoRepository")
@@ -69,68 +68,68 @@ import lombok.val;
 @RequiredArgsConstructor
 //@Log4j2
 public class CommandJdoRepository
-implements CommandModelRepository<CommandJdo> {
+implements PublishedCommandRepository<PublishedCommandForJdo> {
 
     @Inject final Provider<InteractionProvider> interactionProviderProvider;
     @Inject final Provider<RepositoryService> repositoryServiceProvider;
     @Inject final JdoSupportService jdoSupport;
 
     @Override
-    public List<CommandJdo> findByFromAndTo(
+    public List<PublishedCommandForJdo> findByFromAndTo(
             final @Nullable LocalDate from,
             final @Nullable LocalDate to) {
         final Timestamp fromTs = toTimestampStartOfDayWithOffset(from, 0);
         final Timestamp toTs = toTimestampStartOfDayWithOffset(to, 1);
 
-        final Query<CommandJdo> query;
+        final Query<PublishedCommandForJdo> query;
         if(from != null) {
             if(to != null) {
-                query = Query.named(CommandJdo.class, "findByTimestampBetween")
+                query = Query.named(PublishedCommandForJdo.class, "findByTimestampBetween")
                         .withParameter("from", fromTs)
                         .withParameter("to", toTs);
             } else {
-                query = Query.named(CommandJdo.class, "findByTimestampAfter")
+                query = Query.named(PublishedCommandForJdo.class, "findByTimestampAfter")
                         .withParameter("from", fromTs);
             }
         } else {
             if(to != null) {
-                query = Query.named(CommandJdo.class, "findByTimestampBefore")
+                query = Query.named(PublishedCommandForJdo.class, "findByTimestampBefore")
                         .withParameter("to", toTs);
             } else {
-                query = Query.named(CommandJdo.class, "find");
+                query = Query.named(PublishedCommandForJdo.class, "find");
             }
         }
         return repositoryService().allMatches(query);
     }
 
     @Override
-    public Optional<CommandJdo> findByInteractionId(final UUID interactionId) {
+    public Optional<PublishedCommandForJdo> findByInteractionId(final UUID interactionId) {
         return repositoryService().firstMatch(
-                Query.named(CommandJdo.class, "findByInteractionIdStr")
+                Query.named(PublishedCommandForJdo.class, "findByInteractionIdStr")
                     .withParameter("interactionIdStr", interactionId.toString()));
     }
 
     @Override
-    public List<CommandJdo> findByParent(final CommandModel parent) {
+    public List<PublishedCommandForJdo> findByParent(final CommandModel parent) {
         return repositoryService().allMatches(
-                Query.named(CommandJdo.class, "findByParent")
+                Query.named(PublishedCommandForJdo.class, "findByParent")
                     .withParameter("parent", parent));
     }
 
     @Override
-    public List<CommandJdo> findCurrent() {
+    public List<PublishedCommandForJdo> findCurrent() {
         return repositoryService().allMatches(
-                Query.named(CommandJdo.class, "findCurrent"));
+                Query.named(PublishedCommandForJdo.class, "findCurrent"));
     }
 
     @Override
-    public List<CommandJdo> findCompleted() {
+    public List<PublishedCommandForJdo> findCompleted() {
         return repositoryService().allMatches(
-                Query.named(CommandJdo.class, "findCompleted"));
+                Query.named(PublishedCommandForJdo.class, "findCompleted"));
     }
 
     @Override
-    public List<CommandJdo> findByTargetAndFromAndTo(
+    public List<PublishedCommandForJdo> findByTargetAndFromAndTo(
             final Bookmark target,
             final @Nullable LocalDate from,
             final @Nullable LocalDate to) {
@@ -138,25 +137,25 @@ implements CommandModelRepository<CommandJdo> {
         final Timestamp fromTs = toTimestampStartOfDayWithOffset(from, 0);
         final Timestamp toTs = toTimestampStartOfDayWithOffset(to, 1);
 
-        final Query<CommandJdo> query;
+        final Query<PublishedCommandForJdo> query;
         if(from != null) {
             if(to != null) {
-                query = Query.named(CommandJdo.class, "findByTargetAndTimestampBetween")
+                query = Query.named(PublishedCommandForJdo.class, "findByTargetAndTimestampBetween")
                         .withParameter("target", target)
                         .withParameter("from", fromTs)
                         .withParameter("to", toTs);
             } else {
-                query = Query.named(CommandJdo.class, "findByTargetAndTimestampAfter")
+                query = Query.named(PublishedCommandForJdo.class, "findByTargetAndTimestampAfter")
                         .withParameter("target", target)
                         .withParameter("from", fromTs);
             }
         } else {
             if(to != null) {
-                query = Query.named(CommandJdo.class, "findByTargetAndTimestampBefore")
+                query = Query.named(PublishedCommandForJdo.class, "findByTargetAndTimestampBefore")
                         .withParameter("target", target)
                         .withParameter("to", toTs);
             } else {
-                query = Query.named(CommandJdo.class, "findByTarget")
+                query = Query.named(PublishedCommandForJdo.class, "findByTarget")
                         .withParameter("target", target);
             }
         }
@@ -175,42 +174,42 @@ implements CommandModelRepository<CommandJdo> {
     }
 
     @Override
-    public List<CommandJdo> findRecentByUsername(final String username) {
+    public List<PublishedCommandForJdo> findRecentByUsername(final String username) {
         return repositoryService().allMatches(
-                Query.named(CommandJdo.class, "findRecentByUsername")
+                Query.named(PublishedCommandForJdo.class, "findRecentByUsername")
                     .withParameter("username", username));
     }
 
     @Override
-    public List<CommandJdo> findRecentByTarget(final Bookmark target) {
+    public List<PublishedCommandForJdo> findRecentByTarget(final Bookmark target) {
         return repositoryService().allMatches(
-                Query.named(CommandJdo.class, "findRecentByTarget")
+                Query.named(PublishedCommandForJdo.class, "findRecentByTarget")
                     .withParameter("target", target));
     }
 
     @Override
-    public List<CommandJdo> findSince(final UUID interactionId, final Integer batchSize) {
+    public List<PublishedCommandForJdo> findSince(final UUID interactionId, final Integer batchSize) {
         if(interactionId == null) {
             return findFirst();
         }
-        final CommandJdo from = findByInteractionIdElseNull(interactionId);
+        final PublishedCommandForJdo from = findByInteractionIdElseNull(interactionId);
         if(from == null) {
             return Collections.emptyList();
         }
         return findSince(from.getTimestamp(), batchSize);
     }
 
-    private List<CommandJdo> findFirst() {
-        Optional<CommandJdo> firstCommandIfAny = repositoryService().firstMatch(
-                Query.named(CommandJdo.class, "findFirst"));
+    private List<PublishedCommandForJdo> findFirst() {
+        Optional<PublishedCommandForJdo> firstCommandIfAny = repositoryService().firstMatch(
+                Query.named(PublishedCommandForJdo.class, "findFirst"));
         return firstCommandIfAny
                 .map(Collections::singletonList)
                 .orElse(Collections.emptyList());
     }
 
 
-    private CommandJdo findByInteractionIdElseNull(final UUID interactionId) {
-        val tsq = jdoSupport.newTypesafeQuery(CommandJdo.class);
+    private PublishedCommandForJdo findByInteractionIdElseNull(final UUID interactionId) {
+        val tsq = jdoSupport.newTypesafeQuery(PublishedCommandForJdo.class);
         val cand = QCommandJdo.candidate();
         val q = tsq.filter(
                 cand.interactionIdStr.eq(tsq.parameter("interactionIdStr", String.class))
@@ -219,7 +218,7 @@ implements CommandModelRepository<CommandJdo> {
         return q.executeUnique();
     }
 
-    private List<CommandJdo> findSince(
+    private List<PublishedCommandForJdo> findSince(
             final Timestamp timestamp,
             final Integer batchSize) {
 
@@ -227,48 +226,48 @@ implements CommandModelRepository<CommandJdo> {
         // XXX that's a historic workaround, should rather be fixed upstream
         val needsTrimFix = batchSize != null && batchSize == 1;
 
-        val q = Query.named(CommandJdo.class, "findSince")
+        val q = Query.named(PublishedCommandForJdo.class, "findSince")
                 .withParameter("timestamp", timestamp)
                 .withRange(QueryRange.limit(
                         needsTrimFix ? 2L : batchSize
                 ));
 
-        final List<CommandJdo> commandJdos = repositoryService().allMatches(q);
-        return needsTrimFix && commandJdos.size() > 1
-                    ? commandJdos.subList(0,1)
-                    : commandJdos;
+        final List<PublishedCommandForJdo> publishedCommands = repositoryService().allMatches(q);
+        return needsTrimFix && publishedCommands.size() > 1
+                    ? publishedCommands.subList(0,1)
+                    : publishedCommands;
     }
 
 
     @Override
-    public Optional<CommandJdo> findMostRecentReplayed() {
+    public Optional<PublishedCommandForJdo> findMostRecentReplayed() {
 
         return repositoryService().firstMatch(
-                Query.named(CommandJdo.class, "findMostRecentReplayed"));
+                Query.named(PublishedCommandForJdo.class, "findMostRecentReplayed"));
     }
 
     @Override
-    public Optional<CommandJdo> findMostRecentCompleted() {
+    public Optional<PublishedCommandForJdo> findMostRecentCompleted() {
         return repositoryService().firstMatch(
-                Query.named(CommandJdo.class, "findMostRecentCompleted"));
+                Query.named(PublishedCommandForJdo.class, "findMostRecentCompleted"));
     }
 
     @Override
-    public List<CommandJdo> findNotYetReplayed() {
+    public List<PublishedCommandForJdo> findNotYetReplayed() {
         return repositoryService().allMatches(
-                Query.named(CommandJdo.class, "findNotYetReplayed"));
+                Query.named(PublishedCommandForJdo.class, "findNotYetReplayed"));
     }
 
     @Override
-    public List<CommandJdo> findReplayedOnSecondary() {
+    public List<PublishedCommandForJdo> findReplayedOnSecondary() {
         return repositoryService().allMatches(
-                Query.named(CommandJdo.class, "findReplayableMostRecentStarted"));
+                Query.named(PublishedCommandForJdo.class, "findReplayableMostRecentStarted"));
     }
 
     @Override
-    public List<CommandJdo> saveForReplay(final CommandsDto commandsDto) {
+    public List<PublishedCommandForJdo> saveForReplay(final CommandsDto commandsDto) {
         List<CommandDto> commandDto = commandsDto.getCommandDto();
-        List<CommandJdo> commands = new ArrayList<>();
+        List<PublishedCommandForJdo> commands = new ArrayList<>();
         for (final CommandDto dto : commandDto) {
             commands.add(saveForReplay(dto));
         }
@@ -277,7 +276,7 @@ implements CommandModelRepository<CommandJdo> {
 
     @Programmatic
     @Override
-    public CommandJdo saveForReplay(final CommandDto dto) {
+    public PublishedCommandForJdo saveForReplay(final CommandDto dto) {
 
         if(dto.getMember().getInteractionType() == InteractionType.ACTION_INVOCATION) {
             final MapDto userData = dto.getUserData();
@@ -288,32 +287,32 @@ implements CommandModelRepository<CommandJdo> {
             }
         }
 
-        final CommandJdo commandJdo = new CommandJdo();
+        final PublishedCommandForJdo publishedCommand = new PublishedCommandForJdo();
 
-        commandJdo.setInteractionIdStr(dto.getInteractionId());
-        commandJdo.setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(dto.getTimestamp()));
-        commandJdo.setUsername(dto.getUser());
+        publishedCommand.setInteractionIdStr(dto.getInteractionId());
+        publishedCommand.setTimestamp(JavaSqlXMLGregorianCalendarMarshalling.toTimestamp(dto.getTimestamp()));
+        publishedCommand.setUsername(dto.getUser());
 
-        commandJdo.setReplayState(ReplayState.PENDING);
+        publishedCommand.setReplayState(ReplayState.PENDING);
 
         final OidDto firstTarget = dto.getTargets().getOid().get(0);
-        commandJdo.setTarget(Bookmark.forOidDto(firstTarget));
-        commandJdo.setCommandDto(dto);
-        commandJdo.setLogicalMemberIdentifier(dto.getMember().getLogicalMemberIdentifier());
+        publishedCommand.setTarget(Bookmark.forOidDto(firstTarget));
+        publishedCommand.setCommandDto(dto);
+        publishedCommand.setLogicalMemberIdentifier(dto.getMember().getLogicalMemberIdentifier());
 
-        persist(commandJdo);
+        persist(publishedCommand);
 
-        return commandJdo;
+        return publishedCommand;
     }
 
     @Override
-    public void persist(final CommandJdo commandJdo) {
-        repositoryService().persist(commandJdo);
+    public void persist(final PublishedCommandForJdo publishedCommand) {
+        repositoryService().persist(publishedCommand);
     }
 
     @Override
     public void truncateLog() {
-        repositoryService().removeAll(CommandJdo.class);
+        repositoryService().removeAll(PublishedCommandForJdo.class);
     }
 
     private RepositoryService repositoryService() {
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_retry.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_retry.java
deleted file mode 100644
index b58cfa2..0000000
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/CommandJdo_retry.java
+++ /dev/null
@@ -1,70 +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.extensions.commandlog.jdo.entities;
-
-import javax.inject.Inject;
-
-import org.apache.isis.applib.annotation.Action;
-import org.apache.isis.applib.annotation.ActionLayout;
-import org.apache.isis.applib.annotation.Publishing;
-import org.apache.isis.applib.annotation.SemanticsOf;
-import org.apache.isis.applib.services.bookmark.BookmarkService;
-import org.apache.isis.applib.services.command.CommandExecutorService;
-import org.apache.isis.applib.services.metamodel.MetaModelService;
-import org.apache.isis.applib.services.repository.RepositoryService;
-import org.apache.isis.applib.services.xactn.TransactionService;
-import org.apache.isis.applib.services.iactnlayer.InteractionService;
-import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
-import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
-
-import lombok.RequiredArgsConstructor;
-
-@Action(
-    semantics = SemanticsOf.NON_IDEMPOTENT_ARE_YOU_SURE,
-    domainEvent = CommandJdo_retry.ActionDomainEvent.class,
-    commandPublishing = Publishing.DISABLED
-)
-@ActionLayout(associateWith = "executeIn", sequence = "1")
-@RequiredArgsConstructor
-public class CommandJdo_retry {
-
-    private final CommandJdo commandJdo;
-
-    public static class ActionDomainEvent extends IsisModuleExtCommandLogJdo.ActionDomainEvent<CommandJdo_retry> { }
-
-    public CommandJdo act() {
-
-        commandJdo.setReplayState(ReplayState.PENDING);
-        commandJdo.setResult(null);
-        commandJdo.setException((Exception)null);
-        commandJdo.setStartedAt(null);
-        commandJdo.setCompletedAt(null);
-
-        return commandJdo;
-    }
-
-    @Inject InteractionService interactionService;
-    @Inject TransactionService transactionService;
-    @Inject CommandExecutorService commandExecutorService;
-    @Inject RepositoryService repositoryService;
-    @Inject BookmarkService bookmarkService;
-    @Inject MetaModelService metaModelService;
-
-
-}
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdo.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdo.java
new file mode 100644
index 0000000..1eacc4e
--- /dev/null
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/entities/PublishedCommandForJdo.java
@@ -0,0 +1,479 @@
+/*
+ *  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.jdo.entities;
+
+import java.sql.Timestamp;
+import java.util.UUID;
+
+import javax.jdo.annotations.IdentityType;
+
+import org.apache.isis.applib.annotation.DomainObject;
+import org.apache.isis.applib.annotation.DomainObjectLayout;
+import org.apache.isis.applib.mixins.system.DomainChangeRecord;
+import org.apache.isis.applib.services.bookmark.Bookmark;
+import org.apache.isis.applib.types.MemberIdentifierType;
+import org.apache.isis.commons.internal.base._Casts;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
+import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
+import org.apache.isis.schema.cmd.v2.CommandDto;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@javax.jdo.annotations.PersistenceCapable(
+        identityType=IdentityType.APPLICATION,
+        schema = "isisExtensionsCommandLog",
+        table = "PublishedCommand")
+
+// queries that use RANGE 0,2 should instead be RANGE 0,1, however this 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.Queries( {
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_BY_INTERACTION_ID,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE interactionId == :interactionId "),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_BY_PARENT,
+            value="SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE parent == :parent "),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_CURRENT,
+            value="SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE completedAt == null "
+                    + "ORDER BY this.timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_COMPLETED,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE completedAt != null "
+                    + "ORDER BY this.timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_RECENT_BY_TARGET,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE target == :target "
+                    + "ORDER BY this.timestamp DESC "
+                    + "RANGE 0,30"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_BETWEEEN,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE target == :target "
+                    + "&& timestamp >= :from "
+                    + "&& timestamp <= :to "
+                    + "ORDER BY this.timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_AFTER,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE target == :target "
+                    + "&& timestamp >= :from "
+                    + "ORDER BY this.timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_BY_TARGET_AND_TIMESTAMP_BEFORE,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE target == :target "
+                    + "&& timestamp <= :to "
+                    + "ORDER BY this.timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_BY_TARGET,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE target == :target "
+                    + "ORDER BY this.timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_BETWEEN,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE timestamp >= :from "
+                    + "&&    timestamp <= :to "
+                    + "ORDER BY this.timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_AFTER,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE timestamp >= :from "
+                    + "ORDER BY this.timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_BY_TIMESTAMP_BEFORE,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE timestamp <= :to "
+                    + "ORDER BY this.timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " ORDER BY this.timestamp DESC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_BY_USERNAME,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE username == :username "
+                    + "ORDER BY this.timestamp DESC "
+                    + "RANGE 0,30"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_FIRST,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE startedAt   != null "
+                    + "   && completedAt != null "
+                    + "ORDER BY this.timestamp ASC "
+                    + "RANGE 0,2"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_SINCE,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE timestamp > :timestamp "
+                    + "   && startedAt != null "
+                    + "   && completedAt != null "
+                    + "ORDER BY this.timestamp ASC"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_MOST_RECENT_REPLAYED,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE (replayState == 'OK' || replayState == 'FAILED') "
+                    + "ORDER BY this.timestamp DESC "
+                    + "RANGE 0,2"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_MOST_RECENT_COMPLETED,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE startedAt   != null "
+                    + "   && completedAt != null "
+                    + "ORDER BY this.timestamp DESC "
+                    + "RANGE 0,2"),
+    @javax.jdo.annotations.Query(
+            name = PublishedCommand.NAMED_QUERY_FIND_NOT_YET_REPLAYED,
+            value = "SELECT "
+                    + "FROM " + PublishedCommandForJdo.FQCN
+                    + " WHERE replayState == 'PENDING' "
+                    + "ORDER BY this.timestamp ASC "
+                    + "RANGE 0,10"),    // same as batch size
+})
+@javax.jdo.annotations.Indices({
+        @javax.jdo.annotations.Index(name = "CommandJdo__startedAt__timestamp__IDX", members = { "startedAt", "timestamp" }),
+        @javax.jdo.annotations.Index(name = "CommandJdo__timestamp__IDX", members = { "timestamp" }),
+})
+@DomainObject(
+        logicalTypeName = PublishedCommand.LOGICAL_TYPE_NAME
+)
+@DomainObjectLayout(
+        named = "Published Command"
+)
+@NoArgsConstructor(access = AccessLevel.PACKAGE)
+public class PublishedCommandForJdo extends PublishedCommand {
+
+    protected final static String FQCN = "org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo";
+
+    // EVENTS
+    // (see superclass)
+
+    // CONSTRUCTORS
+
+    public PublishedCommandForJdo(
+            final CommandDto commandDto,
+            final ReplayState replayState,
+            final int targetIndex) {
+
+        super(commandDto, replayState, targetIndex);
+    }
+
+
+    // TITLE
+    // (see superclass)
+
+
+    // (CHANGE) TYPE
+    // (derived - see superclass)
+
+
+    // INTERACTION ID
+
+    @javax.jdo.annotations.PrimaryKey
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="false", name = "interactionId", length = 36)
+    private UUID interactionId;
+
+    @DomainChangeRecord.InteractionId
+    @Override
+    public UUID getInteractionId() {
+        return interactionId;
+    }
+    @Override
+    public void setInteractionId(UUID interactionId) {
+        this.interactionId = interactionId;
+    }
+
+
+    // USER NAME
+
+    @javax.jdo.annotations.Column(allowsNull="false", length = 50)
+    private String username;
+
+    @DomainChangeRecord.Username
+    @Override
+    public String getUsername() { return username; }
+    @Override
+    public void setUsername(String username) { this.username = username; }
+
+
+    // TIMESTAMP
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="false")
+    private Timestamp timestamp;
+
+    @DomainChangeRecord.TimestampMeta
+    @Override
+    public Timestamp getTimestamp() {
+        return timestamp;
+    }
+    @Override
+    public void setTimestamp(Timestamp timestamp) {
+        this.timestamp = timestamp;
+    }
+
+
+
+    // REPLAY STATE
+
+    @javax.jdo.annotations.Column(allowsNull="true", length=10)
+    private ReplayState replayState;
+
+    @ReplayStateMeta
+    @Override
+    public ReplayState getReplayState() {
+        return replayState;
+    }
+    @Override
+    public void setReplayState(ReplayState replayState) {
+        this.replayState = replayState;
+    }
+
+
+    // REPLAY STATE FAILURE REASON
+
+    @javax.jdo.annotations.Column(allowsNull="true", length=255)
+    private String replayStateFailureReason;
+
+    @ReplayStateFailureReason
+    @Override
+    public String getReplayStateFailureReason() {
+        return replayStateFailureReason;
+    }
+    @Override
+    public void setReplayStateFailureReason(String replayStateFailureReason) {
+        this.replayStateFailureReason = replayStateFailureReason;
+    }
+
+
+    // PARENT
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(name="parentId", allowsNull="true")
+    private PublishedCommandForJdo parent;
+
+    @Parent
+    @Override
+    public PublishedCommand getParent() {
+        return parent;
+    }
+    @Override
+    public void setParent(PublishedCommand parent) {
+        this.parent = _Casts.uncheckedCast(parent);
+    }
+
+
+
+    // TARGET
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true", length = 2000, name="target")
+    private Bookmark target;
+
+    @DomainChangeRecord.TargetMeta
+    @Override
+    public Bookmark getTarget() {
+        return target;
+    }
+    @Override
+    public void setTarget(Bookmark target) {
+        this.target = target;
+    }
+
+
+    // TARGET MEMBER
+    // (derived - see superclass)
+
+
+
+    // LOCAL MEMBER
+    // (derived - see superclass)
+
+
+
+    // LOGICAL MEMBER IDENTIFIER
+
+    @javax.jdo.annotations.Column(allowsNull="false", length = MemberIdentifierType.Meta.MAX_LEN)
+    @Getter @Setter
+    private String logicalMemberIdentifier;
+
+    @LogicalMemberIdentifier
+    @Override
+    public String getLogicalMemberIdentifier() {
+        return logicalMemberIdentifier;
+    }
+    @Override
+    public void setLogicalMemberIdentifier(String logicalMemberIdentifier) {
+        this.logicalMemberIdentifier = logicalMemberIdentifier;
+    }
+
+
+    // COMMAND DTO
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB")
+    private CommandDto commandDto;
+
+    @CommandDtoMeta
+    @Override
+    public CommandDto getCommandDto() {
+        return commandDto;
+    }
+    @Override
+    public void setCommandDto(CommandDto commandDto) {
+        this.commandDto = commandDto;
+    }
+
+
+    // STARTED AT
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true")
+    private Timestamp startedAt;
+
+    @StartedAt
+    @Override
+    public Timestamp getStartedAt() {
+        return startedAt;
+    }
+    @Override
+    public void setStartedAt(Timestamp startedAt) {
+        this.startedAt = startedAt;
+    }
+
+
+    // COMPLETED AT
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true")
+    private Timestamp completedAt;
+
+    @CompletedAt
+    @Override
+    public Timestamp getCompletedAt() {
+        return completedAt;
+    }
+    @Override
+    public void setCompletedAt(Timestamp completedAt) {
+        this.completedAt = completedAt;
+    }
+
+
+
+    // DURATION
+    // (derived - see superclass)
+
+
+
+    // COMPLETE
+    // (derived - see superclass)
+
+
+
+    // RESULT SUMMARY
+    // (derived - see superclass)
+
+
+
+    // RESULT
+
+    @javax.jdo.annotations.Persistent
+    @javax.jdo.annotations.Column(allowsNull="true", length = 2000, name="result")
+    private Bookmark result;
+
+    @Result
+    @Override
+    public Bookmark getResult() {
+        return result;
+    }
+    @Override
+    public void setResult(Bookmark result) {
+        this.result = result;
+    }
+
+
+    // EXCEPTION
+
+    @javax.jdo.annotations.Column(allowsNull="true", jdbcType="CLOB")
+    private String exception;
+
+    @ExceptionMeta
+    @Override
+    public String getException() {
+        return exception;
+    }
+    @Override
+    public void setException(final String exception) {
+        this.exception = exception;
+    }
+
+
+    // CAUSED EXCEPTION
+    // (derived - see superclass)
+
+
+
+    // PRE VALUE
+    // (derived - see superclass)
+
+
+
+    // POST VALUE
+    // (derived - see superclass)
+
+
+
+    // PROGRAMMATIC
+    // (see superclass)
+
+
+    // TO STRING etc
+    // (see superclass)
+
+
+}
+
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/HasInteractionId_command.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/HasInteractionId_command.java
index b4a236cc..feb4fcc 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/HasInteractionId_command.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/HasInteractionId_command.java
@@ -28,7 +28,7 @@ import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.mixins.system.HasInteractionId;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
-import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo;
+import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo;
 import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
 
 import lombok.RequiredArgsConstructor;
@@ -54,7 +54,7 @@ public class HasInteractionId_command {
 
     private final HasInteractionId hasInteractionId;
 
-    public CommandJdo act() {
+    public PublishedCommandForJdo act() {
         return findCommand();
     }
     /**
@@ -62,13 +62,13 @@ public class HasInteractionId_command {
      * {@link Command#getParent() parent} property.
      */
     public boolean hideAct() {
-        return (hasInteractionId instanceof CommandJdo);
+        return (hasInteractionId instanceof PublishedCommandForJdo);
     }
     public String disableAct() {
         return findCommand() == null ? "No command found for unique Id": null;
     }
 
-    private CommandJdo findCommand() {
+    private PublishedCommandForJdo findCommand() {
         final UUID transactionId = hasInteractionId.getInteractionId();
         return commandServiceRepository
                 .findByInteractionId(transactionId)
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/HasUsername_recentCommandsByUser.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/HasUsername_recentCommandsByUser.java
index 2067ab5..cb59296 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/HasUsername_recentCommandsByUser.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/HasUsername_recentCommandsByUser.java
@@ -27,7 +27,7 @@ import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.applib.mixins.security.HasUsername;
 import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
-import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo;
+import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo;
 import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
 
 
@@ -44,14 +44,14 @@ import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
 public class HasUsername_recentCommandsByUser {
 
     public static class CollectionDomainEvent
-            extends IsisModuleExtCommandLogJdo.CollectionDomainEvent<HasUsername_recentCommandsByUser, CommandJdo> { }
+            extends IsisModuleExtCommandLogJdo.CollectionDomainEvent<HasUsername_recentCommandsByUser, PublishedCommandForJdo> { }
 
     private final HasUsername hasUsername;
     public HasUsername_recentCommandsByUser(final HasUsername hasUsername) {
         this.hasUsername = hasUsername;
     }
 
-    public List<CommandJdo> coll() {
+    public List<PublishedCommandForJdo> coll() {
         final String username = hasUsername.getUsername();
         return username != null
                 ? commandServiceRepository.findRecentByUsername(username)
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/Object_recentCommands.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/Object_recentCommands.java
index fa92dca..85a4401 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/Object_recentCommands.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/Object_recentCommands.java
@@ -33,7 +33,7 @@ import org.apache.isis.applib.mixins.layout.LayoutMixinConstants;
 import org.apache.isis.applib.mixins.system.HasInteractionId;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
-import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo;
+import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo;
 import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
 
 import lombok.RequiredArgsConstructor;
@@ -65,7 +65,7 @@ public class Object_recentCommands {
 
     private final Object domainObject; // mixee
 
-    @MemberSupport public List<CommandJdo> act() {
+    @MemberSupport public List<PublishedCommandForJdo> act() {
         return bookmarkService.bookmarkFor(domainObject)
         .map(commandServiceRepository::findRecentByTarget)
         .orElse(Collections.emptyList());
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/T_recent.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/T_recent.java
index 4fe2d81..7b90bf7 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/T_recent.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/mixins/T_recent.java
@@ -28,7 +28,7 @@ import org.apache.isis.applib.annotation.CollectionLayout;
 import org.apache.isis.applib.services.bookmark.BookmarkService;
 import org.apache.isis.applib.services.queryresultscache.QueryResultsCache;
 import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
-import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo;
+import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo;
 import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
 
 @Collection(
@@ -40,18 +40,18 @@ import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
 public abstract class T_recent<T> {
 
     public static class CollectionDomainEvent
-            extends IsisModuleExtCommandLogJdo.CollectionDomainEvent<T_recent, CommandJdo> { }
+            extends IsisModuleExtCommandLogJdo.CollectionDomainEvent<T_recent, PublishedCommandForJdo> { }
 
     private final T domainObject;
     public T_recent(final T domainObject) {
         this.domainObject = domainObject;
     }
 
-    public List<CommandJdo> coll() {
+    public List<PublishedCommandForJdo> coll() {
         return findRecent();
     }
 
-    private List<CommandJdo> findRecent() {
+    private List<PublishedCommandForJdo> findRecent() {
         return bookmarkService.bookmarkFor(domainObject)
         .map(bookmark->queryResultsCache.execute(
                 () -> commandJdoRepository.findRecentByTarget(bookmark)
diff --git a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/ui/CommandServiceMenu.java b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/ui/CommandServiceMenu.java
index 6bcfefe..2c3d511 100644
--- a/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/ui/CommandServiceMenu.java
+++ b/extensions/core/command-log/jdo/src/main/java/org/apache/isis/extensions/commandlog/jdo/ui/CommandServiceMenu.java
@@ -43,7 +43,7 @@ import org.apache.isis.applib.annotation.RestrictTo;
 import org.apache.isis.applib.annotation.SemanticsOf;
 import org.apache.isis.applib.services.clock.ClockService;
 import org.apache.isis.extensions.commandlog.jdo.IsisModuleExtCommandLogJdo;
-import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdo;
+import org.apache.isis.extensions.commandlog.jdo.entities.PublishedCommandForJdo;
 import org.apache.isis.extensions.commandlog.jdo.entities.CommandJdoRepository;
 
 import lombok.RequiredArgsConstructor;
@@ -81,7 +81,7 @@ public class CommandServiceMenu {
     public static class ActiveCommandsDomainEvent extends ActionDomainEvent { }
     @Action(domainEvent = ActiveCommandsDomainEvent.class, semantics = SemanticsOf.SAFE)
     @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT, cssClassFa = "fa-bolt", sequence="10")
-    public List<CommandJdo> activeCommands() {
+    public List<PublishedCommandForJdo> activeCommands() {
         return commandServiceRepository.findCurrent();
     }
     @MemberSupport public boolean hideActiveCommands() {
@@ -92,7 +92,7 @@ public class CommandServiceMenu {
     public static class FindCommandsDomainEvent extends ActionDomainEvent { }
     @Action(domainEvent = FindCommandsDomainEvent.class, semantics = SemanticsOf.SAFE)
     @ActionLayout(cssClassFa = "fa-search", sequence="20")
-    public List<CommandJdo> findCommands(
+    public List<PublishedCommandForJdo> findCommands(
             @Parameter(optionality= Optionality.OPTIONAL)
             @ParameterLayout(named="From")
             final LocalDate from,
@@ -115,7 +115,7 @@ public class CommandServiceMenu {
     public static class FindCommandByIdDomainEvent extends ActionDomainEvent { }
     @Action(domainEvent = FindCommandByIdDomainEvent.class, semantics = SemanticsOf.SAFE)
     @ActionLayout(cssClassFa = "fa-crosshairs", sequence="30")
-    public CommandJdo findCommandById(
+    public PublishedCommandForJdo findCommandById(
             @ParameterLayout(named="Transaction Id")
             final UUID transactionId) {
         return commandServiceRepository.findByInteractionId(transactionId).orElse(null);
diff --git a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java
index f1d7538..27c19f8 100644
--- a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java
+++ b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpa.java
@@ -34,7 +34,6 @@ import org.springframework.context.event.EventListener;
 import org.springframework.stereotype.Service;
 
 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.MemberSupport;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
@@ -43,7 +42,6 @@ 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.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
-import org.apache.isis.applib.mixins.system.DomainChangeRecord;
 import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.command.CommandOutcomeHandler;
@@ -53,8 +51,8 @@ import org.apache.isis.applib.util.TitleBuffer;
 import org.apache.isis.commons.functional.Result;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.commons.internal.exceptions._Exceptions;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommand;
 import org.apache.isis.extensions.commandlog.jpa.IsisModuleExtCommandLogJpa;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
 import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
 import org.apache.isis.extensions.commandlog.applib.util.BigDecimalUtils;
 import org.apache.isis.extensions.commandlog.applib.util.StringUtils;
@@ -231,19 +229,9 @@ import lombok.val;
         logicalTypeName = CommandJpa.LOGICAL_TYPE_NAME,
         editing = Editing.DISABLED
 )
-@DomainObjectLayout(
-        named = "Command",
-        titleUiEvent = CommandModel.TitleUiEvent.class,
-        iconUiEvent = CommandModel.IconUiEvent.class,
-        cssClassUiEvent = CommandModel.CssClassUiEvent.class,
-        layoutUiEvent = CommandModel.LayoutUiEvent.class
-)
 //@Log4j2
 @NoArgsConstructor
-public class CommandJpa
-implements
-    CommandModel,
-    DomainChangeRecord {
+public class CommandJpa extends PublishedCommand {
 
     public final static String LOGICAL_TYPE_NAME = IsisModuleExtCommandLogJpa.NAMESPACE + ".Command";
     protected final static String FQCN = "org.apache.isis.extensions.commandlog.jpa.entities.CommandJpa";
diff --git a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java
index 8309ed3..f337d44 100644
--- a/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java
+++ b/extensions/core/command-log/jpa/src/main/java/org/apache/isis/extensions/commandlog/jpa/entities/CommandJpaRepository.java
@@ -45,8 +45,7 @@ import org.apache.isis.applib.services.bookmark.Bookmark;
 import org.apache.isis.applib.services.iactn.InteractionProvider;
 import org.apache.isis.applib.services.repository.RepositoryService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModelRepository;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
 import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.CommandsDto;
@@ -68,7 +67,7 @@ import lombok.val;
 @RequiredArgsConstructor
 //@Log4j2
 public class CommandJpaRepository
-implements CommandModelRepository<CommandJpa> {
+implements PublishedCommandRepository<CommandJpa> {
 
     @Inject final Provider<InteractionProvider> interactionProviderProvider;
     @Inject final Provider<RepositoryService> repositoryServiceProvider;
diff --git a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/restapi/CommandRetrievalService.java b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/restapi/CommandRetrievalService.java
index 1f2de45..1c129f9 100644
--- a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/restapi/CommandRetrievalService.java
+++ b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/restapi/CommandRetrievalService.java
@@ -28,8 +28,7 @@ import javax.inject.Named;
 import org.apache.isis.applib.annotation.*;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.exceptions.RecoverableException;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModelRepository;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
 import org.apache.isis.extensions.commandreplay.primary.IsisModuleExtCommandReplayPrimary;
 
 import lombok.Getter;
@@ -78,7 +77,7 @@ public class CommandRetrievalService {
             @ParameterLayout(named="Batch size")
             final Integer batchSize)
             throws NotFoundException {
-        final List<? extends CommandModel> commands = commandModelRepository.findSince(interactionId, batchSize);
+        final List<? extends CommandModel> commands = publishedCommandRepository.findSince(interactionId, batchSize);
         if(commands == null) {
             throw new NotFoundException(interactionId);
         }
@@ -88,6 +87,7 @@ public class CommandRetrievalService {
         return 25;
     }
 
-    @Inject CommandModelRepository<? extends CommandModel> commandModelRepository;
+    @Inject
+    PublishedCommandRepository<? extends CommandModel> publishedCommandRepository;
 }
 
diff --git a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/spiimpl/CaptureResultOfCommand.java b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/spiimpl/CaptureResultOfCommand.java
index 4e433d2..fd53083 100644
--- a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/spiimpl/CaptureResultOfCommand.java
+++ b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/spiimpl/CaptureResultOfCommand.java
@@ -29,7 +29,6 @@ import org.apache.isis.applib.services.command.Command;
 import org.apache.isis.applib.services.commanddto.conmap.UserDataKeys;
 import org.apache.isis.applib.services.commanddto.processor.spi.CommandDtoProcessorService;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 
 import lombok.val;
diff --git a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java
index b9b37ca..6508a86 100644
--- a/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java
+++ b/extensions/core/command-replay/primary/src/main/java/org/apache/isis/extensions/commandreplay/primary/ui/CommandReplayOnPrimaryService.java
@@ -40,8 +40,7 @@ import org.apache.isis.applib.services.commanddto.conmap.ContentMappingServiceFo
 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.applib.dom.CommandModel;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModelRepository;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
 import org.apache.isis.extensions.commandreplay.primary.IsisModuleExtCommandReplayPrimary;
 import org.apache.isis.extensions.commandreplay.primary.restapi.CommandRetrievalService;
 import org.apache.isis.schema.cmd.v2.CommandDto;
@@ -69,7 +68,7 @@ public class CommandReplayOnPrimaryService {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleExtCommandReplayPrimary.NAMESPACE + ".CommandReplayOnPrimaryService";
 
-    @Inject final CommandModelRepository<? extends CommandModel> commandModelRepository;
+    @Inject final PublishedCommandRepository<? extends CommandModel> publishedCommandRepository;
     @Inject final JaxbService jaxbService;
     @Inject final MessageService messageService;
     @Inject final ContentMappingServiceForCommandsDto contentMappingServiceForCommandsDto;
@@ -141,7 +140,7 @@ public class CommandReplayOnPrimaryService {
                 @Nullable
                 final Integer batchSize,
                 final String filenamePrefix) {
-            final List<? extends CommandModel> commands = commandModelRepository.findSince(interactionId, batchSize);
+            final List<? extends CommandModel> commands = publishedCommandRepository.findSince(interactionId, batchSize);
             if(commands == null) {
                 messageService.informUser("No commands found");
             }
@@ -183,7 +182,7 @@ public class CommandReplayOnPrimaryService {
                 final UUID interactionId,
                 final String filenamePrefix) {
 
-            return commandModelRepository.findByInteractionId(interactionId)
+            return publishedCommandRepository.findByInteractionId(interactionId)
                     .map(commandJdo -> {
 
                         final CommandDto commandDto = commandJdo.getCommandDto();
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyser.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyser.java
index b86172d..4d6a0fe 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyser.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyser.java
@@ -18,8 +18,6 @@
  */
 package org.apache.isis.extensions.commandreplay.secondary.analyser;
 
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
-
 /**
  * @since 2.0 {@index}
  */
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyserException.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyserException.java
index abb0252..e0c6ed3 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyserException.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyserException.java
@@ -30,7 +30,6 @@ import org.apache.isis.applib.services.commanddto.conmap.UserDataKeys;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
 import org.apache.isis.commons.internal.base._Strings;
 import org.apache.isis.core.config.IsisConfiguration;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
 import org.apache.isis.schema.common.v2.InteractionType;
 
 import lombok.RequiredArgsConstructor;
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyserResult.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyserResult.java
index 5f7695f..ad94192 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyserResult.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analyser/CommandReplayAnalyserResult.java
@@ -29,7 +29,6 @@ import org.springframework.stereotype.Service;
 import org.apache.isis.applib.services.commanddto.conmap.UserDataKeys;
 import org.apache.isis.applib.util.schema.CommandDtoUtils;
 import org.apache.isis.core.config.IsisConfiguration;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
 import org.apache.isis.schema.common.v2.InteractionType;
 
 import lombok.RequiredArgsConstructor;
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analysis/CommandReplayAnalysisService.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analysis/CommandReplayAnalysisService.java
index c2fe4e3..6679cd0 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analysis/CommandReplayAnalysisService.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/analysis/CommandReplayAnalysisService.java
@@ -26,7 +26,6 @@ import javax.inject.Named;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.springframework.stereotype.Service;
 
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
 import org.apache.isis.extensions.commandreplay.secondary.analyser.CommandReplayAnalyser;
 
 import lombok.extern.log4j.Log4j2;
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher.java
index 472d291..316892a 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher.java
@@ -34,7 +34,6 @@ import org.springframework.stereotype.Service;
 
 import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.services.jaxb.JaxbService.Simple;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
 import org.apache.isis.extensions.commandreplay.secondary.SecondaryStatus;
 import org.apache.isis.extensions.commandreplay.secondary.StatusException;
 import org.apache.isis.extensions.commandreplay.secondary.config.SecondaryConfig;
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java
index c4b7c1f..27d0332 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/jobcallables/ReplicateAndRunCommands.java
@@ -28,8 +28,7 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.services.command.CommandExecutorService;
 import org.apache.isis.applib.services.xactn.TransactionService;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModelRepository;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
 import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
 import org.apache.isis.extensions.commandreplay.secondary.SecondaryStatus;
 import org.apache.isis.extensions.commandreplay.secondary.StatusException;
@@ -58,7 +57,8 @@ public class ReplicateAndRunCommands implements Callable<SecondaryStatus> {
     @Inject CommandExecutorService commandExecutorService;
     @Inject TransactionService transactionService;
     @Inject CommandFetcher commandFetcher;
-    @Inject CommandModelRepository<? extends CommandModel> commandModelRepository;
+    @Inject
+    PublishedCommandRepository<? extends CommandModel> publishedCommandRepository;
     @Inject CommandReplayAnalysisService analysisService;
     @Inject Optional<ReplayCommandExecutionController> controller;
 
@@ -85,12 +85,12 @@ public class ReplicateAndRunCommands implements Callable<SecondaryStatus> {
 
             // is there a pending command already?
             // (we fetch several at a time, so we may not have processed them all yet)
-            commandsToReplay = commandModelRepository.findNotYetReplayed();
+            commandsToReplay = publishedCommandRepository.findNotYetReplayed();
 
             if(commandsToReplay.isEmpty()) {
 
                 // look for previously replayed on secondary
-                CommandModel hwm = commandModelRepository.findMostRecentReplayed().orElse(null);
+                CommandModel hwm = publishedCommandRepository.findMostRecentReplayed().orElse(null);
 
                 if (hwm != null) {
                     // give up if there was a failure; admin will need to fix issue and retry
@@ -103,7 +103,7 @@ public class ReplicateAndRunCommands implements Callable<SecondaryStatus> {
                     // after a DB restore from primary to secondary, there won't be
                     // any that have been replayed.  So instead we simply use
                     // latest completed (on primary) as the HWM.
-                    hwm = commandModelRepository.findMostRecentCompleted().orElse(null);
+                    hwm = publishedCommandRepository.findMostRecentCompleted().orElse(null);
                 }
 
                 // fetch next command(s) from primary (if any)
@@ -111,7 +111,7 @@ public class ReplicateAndRunCommands implements Callable<SecondaryStatus> {
                 commandsToReplay = commandDtos.stream()
                         .map(dto ->
                                 transactionService.callWithinCurrentTransactionElseCreateNew(
-                                    () -> commandModelRepository.saveForReplay(dto))
+                                    () -> publishedCommandRepository.saveForReplay(dto))
                                 .optionalElseFail()
                                 .orElse(null)
                         )
@@ -154,7 +154,7 @@ public class ReplicateAndRunCommands implements Callable<SecondaryStatus> {
 
             val childCommands =
                     transactionService.callWithinCurrentTransactionElseCreateNew(
-                            () -> commandModelRepository.findByParent(parent))
+                            () -> publishedCommandRepository.findByParent(parent))
                     .optionalElseFail()
                     .orElse(Collections.emptyList());
             for (val childCommand : childCommands) {
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandJdo_replayQueue.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandJdo_replayQueue.java
index b148baf..8b119d1 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandJdo_replayQueue.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandJdo_replayQueue.java
@@ -24,8 +24,7 @@ import javax.inject.Inject;
 
 import org.apache.isis.applib.annotation.Collection;
 import org.apache.isis.applib.annotation.CollectionLayout;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModelRepository;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
 import org.apache.isis.extensions.commandreplay.secondary.IsisModuleExtCommandReplaySecondary;
 import org.apache.isis.extensions.commandreplay.secondary.config.SecondaryConfig;
 
@@ -50,13 +49,14 @@ public class CommandJdo_replayQueue {
     final CommandModel commandModel;
 
     public List<? extends CommandModel> coll() {
-        return commandModelRepository.findReplayedOnSecondary();
+        return publishedCommandRepository.findReplayedOnSecondary();
     }
     public boolean hideColl() {
         return !secondaryConfig.isConfigured();
     }
 
     @Inject SecondaryConfig secondaryConfig;
-    @Inject CommandModelRepository<? extends CommandModel> commandModelRepository;
+    @Inject
+    PublishedCommandRepository<? extends CommandModel> publishedCommandRepository;
 
 }
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandModel_exclude.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandModel_exclude.java
index ec04041..9cfcbc6 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandModel_exclude.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/mixins/CommandModel_exclude.java
@@ -26,7 +26,6 @@ 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.extensions.commandlog.applib.IsisModuleExtCommandLogApplib;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
 import org.apache.isis.extensions.commandlog.applib.dom.ReplayState;
 import org.apache.isis.extensions.commandreplay.secondary.config.SecondaryConfig;
 
diff --git a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java
index f80aa46..c096938 100644
--- a/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java
+++ b/extensions/core/command-replay/secondary/src/main/java/org/apache/isis/extensions/commandreplay/secondary/ui/CommandReplayOnSecondaryService.java
@@ -28,8 +28,7 @@ import org.apache.isis.applib.annotation.*;
 import org.apache.isis.applib.annotation.PriorityPrecedence;
 import org.apache.isis.applib.services.jaxb.JaxbService;
 import org.apache.isis.applib.value.Clob;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModel;
-import org.apache.isis.extensions.commandlog.applib.dom.CommandModelRepository;
+import org.apache.isis.extensions.commandlog.applib.dom.PublishedCommandRepository;
 import org.apache.isis.extensions.commandreplay.secondary.IsisModuleExtCommandReplaySecondary;
 import org.apache.isis.schema.cmd.v2.CommandDto;
 import org.apache.isis.schema.cmd.v2.CommandsDto;
@@ -56,7 +55,8 @@ public class CommandReplayOnSecondaryService {
 
     public static final String LOGICAL_TYPE_NAME = IsisModuleExtCommandReplaySecondary.NAMESPACE + ".CommandReplayOnSecondaryService";
 
-    @Inject CommandModelRepository<? extends CommandModel> commandModelRepository;
+    @Inject
+    PublishedCommandRepository<? extends CommandModel> publishedCommandRepository;
     @Inject final JaxbService jaxbService;
 
     public static abstract class ActionDomainEvent<T> extends IsisModuleExtCommandReplaySecondary.ActionDomainEvent<T> { }
@@ -68,7 +68,7 @@ public class CommandReplayOnSecondaryService {
         public class ActionEvent extends ActionDomainEvent<findMostRecentReplayed> { }
 
         @MemberSupport public CommandModel act() {
-            return commandModelRepository.findMostRecentReplayed().orElse(null);
+            return publishedCommandRepository.findMostRecentReplayed().orElse(null);
         }
     }
 
@@ -96,7 +96,7 @@ public class CommandReplayOnSecondaryService {
             }
 
             for (final CommandDto commandDto : commandDtoList) {
-                commandModelRepository.saveForReplay(commandDto);
+                publishedCommandRepository.saveForReplay(commandDto);
             }
         }
 
diff --git a/extensions/core/command-replay/secondary/src/test/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher_Test.java b/extensions/core/command-replay/secondary/src/test/java/org/apache/isis/extensions/commandreplay/secondary/fetch/PublishedCommandFetcher_Test.java
similarity index 97%
rename from extensions/core/command-replay/secondary/src/test/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher_Test.java
rename to extensions/core/command-replay/secondary/src/test/java/org/apache/isis/extensions/commandreplay/secondary/fetch/PublishedCommandFetcher_Test.java
index 75c67a6..1df9825 100644
--- a/extensions/core/command-replay/secondary/src/test/java/org/apache/isis/extensions/commandreplay/secondary/fetch/CommandFetcher_Test.java
+++ b/extensions/core/command-replay/secondary/src/test/java/org/apache/isis/extensions/commandreplay/secondary/fetch/PublishedCommandFetcher_Test.java
@@ -35,7 +35,7 @@ import org.apache.isis.schema.cmd.v2.CommandsDto;
 import lombok.val;
 
 
-public class CommandFetcher_Test {
+public class PublishedCommandFetcher_Test {
 
     @Disabled // intended only for manual verification.
     @Test
diff --git a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/ApplicationRole.java b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/ApplicationRole.java
index 70c6c25..9e7204d 100644
--- a/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/ApplicationRole.java
+++ b/extensions/security/secman/applib/src/main/java/org/apache/isis/extensions/secman/applib/role/dom/ApplicationRole.java
@@ -71,7 +71,7 @@ public abstract class ApplicationRole implements Comparable<ApplicationRole> {
 
     // -- MODEL
 
-    @Title
+    @ObjectSupport
     public String title() {
         return getName();
     }
diff --git a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/role/dom/ApplicationRole.java b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/role/dom/ApplicationRole.java
index 57ab64b..a2c8d94 100644
--- a/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/role/dom/ApplicationRole.java
+++ b/extensions/security/secman/persistence-jdo/src/main/java/org/apache/isis/extensions/secman/jdo/role/dom/ApplicationRole.java
@@ -71,10 +71,10 @@ import org.apache.isis.extensions.secman.applib.user.dom.ApplicationUser;
         logicalTypeName = ApplicationRole.LOGICAL_TYPE_NAME,
         autoCompleteRepository = ApplicationRoleRepository.class,
         autoCompleteMethod = "findMatching"
-        )
+)
 @DomainObjectLayout(
         bookmarking = BookmarkPolicy.AS_ROOT
-        )
+)
 public class ApplicationRole
     extends org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole {
 
diff --git a/extensions/security/secman/persistence-jpa/src/main/java/org/apache/isis/extensions/secman/jpa/role/dom/ApplicationRole.java b/extensions/security/secman/persistence-jpa/src/main/java/org/apache/isis/extensions/secman/jpa/role/dom/ApplicationRole.java
index 0155444..21eb262 100644
--- a/extensions/security/secman/persistence-jpa/src/main/java/org/apache/isis/extensions/secman/jpa/role/dom/ApplicationRole.java
+++ b/extensions/security/secman/persistence-jpa/src/main/java/org/apache/isis/extensions/secman/jpa/role/dom/ApplicationRole.java
@@ -69,10 +69,10 @@ import org.apache.isis.persistence.jpa.applib.integration.IsisEntityListener;
         logicalTypeName = ApplicationRole.LOGICAL_TYPE_NAME,
         autoCompleteRepository = ApplicationRoleRepository.class,
         autoCompleteMethod = "findMatching"
-        )
+)
 @DomainObjectLayout(
         bookmarking = BookmarkPolicy.AS_ROOT
-        )
+)
 public class ApplicationRole
     extends org.apache.isis.extensions.secman.applib.role.dom.ApplicationRole {
 
diff --git a/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/jdoql-and-timestamps.adoc b/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/jdoql-and-timestamps.adoc
index ba667fa..fe6586f 100644
--- a/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/jdoql-and-timestamps.adoc
+++ b/persistence/jdo/adoc/modules/ROOT/pages/hints-and-tips/jdoql-and-timestamps.adoc
@@ -6,7 +6,7 @@
 
 Beware of entities with a property called "timestamp": you run the risk of "timestamp" being treated as a keyword in certain contexts, probably not as you intended.
 
-By way of example, the xref:userguide:command-log:about.adoc[Command Log] module has an entity called `CommandJdo`.
+By way of example, the xref:userguide:command-log:about.adoc[Command Log] module has an entity called `PublishedCommandForJdo`.
 This has a property called "timestamp", of type `java.sql.Timestamp`.
 
 This defines a query using JDOQL: