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

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


The following commit(s) were added to refs/heads/main by this push:
     new 1f8b579df7e CAUSEWAY-3982: extend auditing to only log on updates, not 
create or delete (forwardport from v2 to v4)
1f8b579df7e is described below

commit 1f8b579df7ec0a3a84e41f07898c0db46f6ebfdb
Author: andi-huber <[email protected]>
AuthorDate: Fri Mar 27 06:36:07 2026 +0100

    CAUSEWAY-3982: extend auditing to only log on updates, not create or
    delete (forwardport from v2 to v4)
---
 .../causeway/applib/annotation/Publishing.java     |  21 ++-
 .../CommandPublishingFacetForActionAnnotation.java |  55 +++----
 ...ommandPublishingFacetForPropertyAnnotation.java |  80 +++++-----
 ...xecutionPublishingFacetForActionAnnotation.java |  46 +++---
 ...cutionPublishingFacetForPropertyAnnotation.java |  82 +++++-----
 ...gePublishingFacetForDomainObjectAnnotation.java |  35 ++---
 ...FacetForDomainObjectAnnotationAsConfigured.java |   2 +-
 ...tityChangePublishingFacetFromConfiguration.java |   2 +-
 .../entitychange/EntityChangePublishingFacet.java  |  45 ++++--
 .../EntityChangePublishingFacetAbstract.java       |  12 +-
 .../EntityPropertyChangePublishingPolicyFacet.java |  11 +-
 ...PublishingPolicyFacetForPropertyAnnotation.java |   6 +-
 .../metamodel/facets/FacetFactoryTestAbstract.java |   3 +-
 .../DomainObjectAnnotationFacetFactoryTest.java    | 165 +++++++++++++--------
 .../PropertyAnnotationFacetFactoryTest.java        |   5 +-
 .../changetracking/EntityChangeTrackerDefault.java |  44 +++---
 16 files changed, 325 insertions(+), 289 deletions(-)

diff --git 
a/api/applib/src/main/java/org/apache/causeway/applib/annotation/Publishing.java
 
b/api/applib/src/main/java/org/apache/causeway/applib/annotation/Publishing.java
index b0fcc085537..c6125513cdb 100644
--- 
a/api/applib/src/main/java/org/apache/causeway/applib/annotation/Publishing.java
+++ 
b/api/applib/src/main/java/org/apache/causeway/applib/annotation/Publishing.java
@@ -45,8 +45,8 @@ public enum Publishing {
      * Publishing of data triggered by interaction with this object
      * should be handled as per the default publishing policy
      * configured in <tt>application.properties</tt>.
-     * <p>
-     * If no publishing policy is configured, then publishing is disabled.
+     *
+     * <p>If no publishing policy is configured, then publishing is disabled.
      */
     AS_CONFIGURED,
 
@@ -55,6 +55,23 @@ public enum Publishing {
      */
     ENABLED,
 
+    /**
+     * Applies only to {@link EntityPropertyChangeSubscriber}, whereby events 
are published for modifications to the
+     * object, but no events are published for the initial creation of an 
object.
+     *
+     * <p>In the case of audit trail extension,
+     * this effectively suppresses all of the "[NEW] -> value" entries that 
are created for every property of the
+     * entity when it is being created, and also all of the "value -> 
[DELETED]" entries that are created for every property of the
+     * entity when it is being deleted.
+     *
+     * <p>This variant is intended only where the application code has enough 
traceability built into the domain
+     * (perhaps to provide visibility to the end-users) that the technical 
auditing is overkill.  It will also
+     * of course reduce the volume of auditing, so improves performance 
(likely both response times and throughput).
+     *
+     * <p>For other subscribers, behaviour is the same as {@link #ENABLED}.
+     */
+    ENABLED_FOR_UPDATES_ONLY,
+
     /**
      * Do <b>not</b> publish data triggered by interaction with this object
      * (even if otherwise configured to enable publishing).
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacetForActionAnnotation.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacetForActionAnnotation.java
index c4ec4abb732..b7f35a5f8ae 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacetForActionAnnotation.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacetForActionAnnotation.java
@@ -62,40 +62,33 @@ public static Optional<CommandPublishingFacet> create(
         var publishingPolicy = 
ActionConfigOptions.actionCommandPublishingPolicy(configuration);
 
         return actionsIfAny
-                .filter(action -> action.commandPublishing() != 
Publishing.NOT_SPECIFIED)
-                .map(action -> {
-                    Publishing publishing = action.commandPublishing();
+            .filter(action -> action.commandPublishing() != 
Publishing.NOT_SPECIFIED)
+            .map(action -> {
+                Publishing publishing = action.commandPublishing();
 
-                    final Class<? extends CommandDtoProcessor> processorClass 
= action.commandDtoProcessor();
-                    final CommandDtoProcessor processor = 
newProcessorElseNull(processorClass);
+                final Class<? extends CommandDtoProcessor> processorClass = 
action.commandDtoProcessor();
+                final CommandDtoProcessor processor = 
newProcessorElseNull(processorClass);
 
-                    if(processor != null) {
-                        publishing = Publishing.ENABLED;
-                    }
+                if(processor != null) {
+                    publishing = Publishing.ENABLED;
+                }
 
-                    switch (publishing) {
-                        case AS_CONFIGURED:
-                            switch (publishingPolicy) {
-                                case NONE:
-                                    return new 
CommandPublishingFacetForActionAnnotationAsConfigured.None(holder, 
servicesInjector);
-                                case IGNORE_QUERY_ONLY:
-                                case IGNORE_SAFE:
-                                    return Facets.hasSafeSemantics(holder)
-                                            ? new 
CommandPublishingFacetForActionAnnotationAsConfigured.IgnoreSafe(holder, 
servicesInjector)
-                                            : new 
CommandPublishingFacetForActionAnnotationAsConfigured.IgnoreSafeYetNot(holder, 
servicesInjector);
-                                case ALL:
-                                    return new 
CommandPublishingFacetForActionAnnotationAsConfigured.All(holder, 
servicesInjector);
-                                default:
-                                    throw new 
IllegalStateException(String.format("configured action.commandPublishing policy 
'%s' not recognised", publishingPolicy));
-                            }
-                        case DISABLED:
-                            return new 
CommandPublishingFacetForActionAnnotation.Disabled(processor, holder, 
servicesInjector);
-                        case ENABLED:
-                            return new 
CommandPublishingFacetForActionAnnotation.Enabled(processor, holder, 
servicesInjector);
-                        default:
-                            throw new 
IllegalStateException(String.format("@Action#commandPublishing '%s' not 
recognised", publishing));
-                    }
-                });
+                return switch (publishing) {
+                    case AS_CONFIGURED -> switch (publishingPolicy) {
+                        case NONE -> new 
CommandPublishingFacetForActionAnnotationAsConfigured.None(holder, 
servicesInjector);
+                        case IGNORE_QUERY_ONLY, IGNORE_SAFE -> 
Facets.hasSafeSemantics(holder)
+                            ? new 
CommandPublishingFacetForActionAnnotationAsConfigured.IgnoreSafe(holder, 
servicesInjector)
+                            : new 
CommandPublishingFacetForActionAnnotationAsConfigured.IgnoreSafeYetNot(holder, 
servicesInjector);
+                        case ALL -> new 
CommandPublishingFacetForActionAnnotationAsConfigured.All(holder, 
servicesInjector);
+                        default -> throw new IllegalStateException(
+                                String.format("configured 
action.commandPublishing policy '%s' not recognised", publishingPolicy));
+                    };
+                    case DISABLED -> new 
CommandPublishingFacetForActionAnnotation.Disabled(processor, holder, 
servicesInjector);
+                    case ENABLED, ENABLED_FOR_UPDATES_ONLY -> new 
CommandPublishingFacetForActionAnnotation.Enabled(processor, holder, 
servicesInjector);
+                    default -> throw new IllegalStateException(
+                            String.format("@Action#commandPublishing '%s' not 
recognised", publishing));
+                };
+            });
     }
 
     CommandPublishingFacetForActionAnnotation(
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacetForPropertyAnnotation.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacetForPropertyAnnotation.java
index 873470d9746..642a9edd462 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacetForPropertyAnnotation.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/command/CommandPublishingFacetForPropertyAnnotation.java
@@ -68,7 +68,7 @@ public static CommandPublishingFacet create(
 
         return propertyIfAny
             .filter(property -> property.commandPublishing() != 
Publishing.NOT_SPECIFIED)
-            .map(property -> {
+            .<CommandPublishingFacet>map(property -> {
                 Publishing publishing = property.commandPublishing();
 
                 var processorClass = property.commandDtoProcessor();
@@ -78,64 +78,54 @@ public static CommandPublishingFacet create(
                     publishing = Publishing.ENABLED;
                 }
 
-                switch (publishing) {
-                    case AS_CONFIGURED:
-                        switch (publishingPolicy) {
-                            case NONE:
-                                return (CommandPublishingFacet)new 
CommandPublishingFacetForPropertyAnnotationAsConfigured.None(holder, 
servicesInjector);
-                            case ALL:
-                                return new 
CommandPublishingFacetForPropertyAnnotationAsConfigured.All(holder, 
servicesInjector);
-                            default:
-                                throw new 
IllegalStateException(String.format("configured property.commandpublishing 
policy '%s' not recognised", publishingPolicy));
-                        }
-                    case DISABLED:
-                        return new 
CommandPublishingFacetForPropertyAnnotation.Disabled(processor, holder, 
servicesInjector);
-                    case ENABLED:
-                        return new 
CommandPublishingFacetForPropertyAnnotation.Enabled(processor, holder, 
servicesInjector);
-                    default:
-                        throw new 
IllegalStateException(String.format("@Property#commandPublishing '%s' not 
recognised", publishing));
-                }
+                return switch (publishing) {
+                    case AS_CONFIGURED -> switch (publishingPolicy) {
+                        case NONE -> new 
CommandPublishingFacetForPropertyAnnotationAsConfigured.None(holder, 
servicesInjector);
+                        case ALL -> new 
CommandPublishingFacetForPropertyAnnotationAsConfigured.All(holder, 
servicesInjector);
+                        default -> throw new IllegalStateException(
+                                String.format("configured 
property.commandpublishing policy '%s' not recognised", publishingPolicy));
+                    };
+                    case DISABLED -> new 
CommandPublishingFacetForPropertyAnnotation.Disabled(processor, holder, 
servicesInjector);
+                    case ENABLED, ENABLED_FOR_UPDATES_ONLY -> new 
CommandPublishingFacetForPropertyAnnotation.Enabled(processor, holder, 
servicesInjector);
+                    default -> throw new IllegalStateException(
+                            String.format("@Property#commandPublishing '%s' 
not recognised", publishing));
+                };
             })
             .orElseGet(() -> {
                 // there is no publishing facet from either @Action or 
@Property, so use the appropriate configuration to install a default
-                if (representsProperty(holder)) {
+                if (representsProperty(holder))
                     // we are dealing with a property
-                    switch (publishingPolicy) {
-                        case NONE:
-                            return new 
CommandPublishingFacetForPropertyFromConfiguration.None(holder, 
servicesInjector);
-                        case ALL:
-                            return new 
CommandPublishingFacetForPropertyFromConfiguration.All(holder, 
servicesInjector);
-                        default:
-                            throw new 
IllegalStateException(String.format("configured property.commandPublishing 
policy '%s' not recognised", publishingPolicy));
-                    }
-                } else {
+                    return switch (publishingPolicy) {
+                        case NONE -> new 
CommandPublishingFacetForPropertyFromConfiguration.None(holder, 
servicesInjector);
+                        case ALL -> new 
CommandPublishingFacetForPropertyFromConfiguration.All(holder, 
servicesInjector);
+                        default -> throw new 
IllegalStateException(String.format("configured property.commandPublishing 
policy '%s' not recognised", publishingPolicy));
+                    };
+                else {
                     // we are dealing with an action
                     var actionPublishingPolicy = 
ActionConfigOptions.actionCommandPublishingPolicy(configuration);
-                    switch (actionPublishingPolicy) {
-                        case NONE:
-                            return new 
CommandPublishingFacetForActionFromConfiguration.None(holder, servicesInjector);
-                        case IGNORE_QUERY_ONLY:
-                        case IGNORE_SAFE:
-                            return Facets.hasSafeSemantics(holder)
-                                    ? new 
CommandPublishingFacetForActionFromConfiguration.IgnoreSafe(holder, 
servicesInjector)
-                                    : new 
CommandPublishingFacetForActionFromConfiguration.IgnoreSafeYetNot(holder, 
servicesInjector);
-                        case ALL:
-                            return new 
CommandPublishingFacetForActionFromConfiguration.All(holder, servicesInjector);
-                        default:
-                            throw new 
IllegalStateException(String.format("configured action.commandPublishing policy 
'%s' not recognised", actionPublishingPolicy));
-                    }
+                    return switch (actionPublishingPolicy) {
+                        case NONE -> new 
CommandPublishingFacetForActionFromConfiguration.None(holder, servicesInjector);
+                        case IGNORE_QUERY_ONLY, IGNORE_SAFE -> 
Facets.hasSafeSemantics(holder)
+                                                            ? new 
CommandPublishingFacetForActionFromConfiguration.IgnoreSafe(holder, 
servicesInjector)
+                                                            : new 
CommandPublishingFacetForActionFromConfiguration.IgnoreSafeYetNot(holder, 
servicesInjector);
+                        case ALL -> new 
CommandPublishingFacetForActionFromConfiguration.All(holder, servicesInjector);
+                        default -> throw new 
IllegalStateException(String.format("configured action.commandPublishing policy 
'%s' not recognised", actionPublishingPolicy));
+                    };
                 }
             });
+
     }
 
     private static boolean representsProperty(final FacetHolder holder) {
         // a property
-        if (holder instanceof TypedFacetHolder && 
((TypedFacetHolder)holder).featureType() == FeatureType.PROPERTY) {
+        if (holder instanceof TypedFacetHolder typedFacetHolder
+                && typedFacetHolder.featureType() == FeatureType.PROPERTY)
             return true;
-        }
         // or a mixin
-        return  holder.containsFacet(ContributingFacet.class) &&
-                holder.getFacet(ContributingFacet.class).contributed() == 
MixinFacet.Contributing.AS_PROPERTY;
+        return holder.lookupFacet(ContributingFacet.class)
+                .map(ContributingFacet::contributed)
+                .map(MixinFacet.Contributing.AS_PROPERTY::equals)
+                .orElse(false);
     }
 
     CommandPublishingFacetForPropertyAnnotation(
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/execution/ExecutionPublishingFacetForActionAnnotation.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/execution/ExecutionPublishingFacetForActionAnnotation.java
index 14b780a8d5f..67b52d6bdb8 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/execution/ExecutionPublishingFacetForActionAnnotation.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/execution/ExecutionPublishingFacetForActionAnnotation.java
@@ -58,34 +58,24 @@ public static Optional<ExecutionPublishingFacet> create(
 
         var publishingPolicy = 
ActionConfigOptions.actionExecutionPublishingPolicy(configuration);
 
-        return
-        actionsIfAny
-                .filter(action -> action.executionPublishing() != 
Publishing.NOT_SPECIFIED)
-                .map(Action::executionPublishing)
-                .<ExecutionPublishingFacet>map(publishing -> {
-                    switch (publishing) {
-                        case AS_CONFIGURED:
-                            switch (publishingPolicy) {
-                                case NONE:
-                                    return new 
ExecutionPublishingFacetForActionAnnotationAsConfigured.None(holder);
-                                case IGNORE_QUERY_ONLY:
-                                case IGNORE_SAFE:
-                                    return Facets.hasSafeSemantics(holder)
-                                            ? new 
ExecutionPublishingFacetForActionAnnotationAsConfigured.IgnoreSafe(holder)
-                                            : new 
ExecutionPublishingFacetForActionAnnotationAsConfigured.IgnoreSafeYetNot(holder);
-                                case ALL:
-                                    return new 
ExecutionPublishingFacetForActionAnnotationAsConfigured.All(holder);
-                                default:
-                                    throw new 
IllegalStateException(String.format("configured action.executionPublishing 
policy '%s' not recognised", publishingPolicy));
-                                }
-                        case DISABLED:
-                            return new 
ExecutionPublishingFacetForActionAnnotation.Disabled(holder);
-                        case ENABLED:
-                            return new 
ExecutionPublishingFacetForActionAnnotation.Enabled(holder);
-                        default:
-                            throw new 
IllegalStateException(String.format("@Action#executionPublishing '%s' not 
recognised", publishing));
-                    }
-                });
+        return actionsIfAny
+            .filter(action -> action.executionPublishing() != 
Publishing.NOT_SPECIFIED)
+            .map(Action::executionPublishing)
+            .<ExecutionPublishingFacet>map(publishing -> (switch (publishing) {
+                case AS_CONFIGURED -> switch (publishingPolicy) {
+                    case NONE -> new 
ExecutionPublishingFacetForActionAnnotationAsConfigured.None(holder);
+                    case IGNORE_QUERY_ONLY, IGNORE_SAFE -> 
Facets.hasSafeSemantics(holder)
+                        ? new 
ExecutionPublishingFacetForActionAnnotationAsConfigured.IgnoreSafe(holder)
+                        : new 
ExecutionPublishingFacetForActionAnnotationAsConfigured.IgnoreSafeYetNot(holder);
+                    case ALL -> new 
ExecutionPublishingFacetForActionAnnotationAsConfigured.All(holder);
+                    default -> throw new IllegalStateException(
+                            String.format("configured 
action.executionPublishing policy '%s' not recognised", publishingPolicy));
+                };
+                case DISABLED -> new 
ExecutionPublishingFacetForActionAnnotation.Disabled(holder);
+                case ENABLED, ENABLED_FOR_UPDATES_ONLY -> new 
ExecutionPublishingFacetForActionAnnotation.Enabled(holder);
+                default -> throw new IllegalStateException(
+                        String.format("@Action#executionPublishing '%s' not 
recognised", publishing));
+            }));
     }
 
     ExecutionPublishingFacetForActionAnnotation(final FacetHolder holder) {
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/execution/ExecutionPublishingFacetForPropertyAnnotation.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/execution/ExecutionPublishingFacetForPropertyAnnotation.java
index ec9fcc705df..f08b54d6e34 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/execution/ExecutionPublishingFacetForPropertyAnnotation.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/members/publish/execution/ExecutionPublishingFacetForPropertyAnnotation.java
@@ -67,66 +67,54 @@ public static ExecutionPublishingFacet create(
         return propertyIfAny
             .map(Property::executionPublishing)
             .filter(publishing -> publishing != Publishing.NOT_SPECIFIED)
-            .map(publishing -> {
-
-                switch (publishing) {
-                    case AS_CONFIGURED:
-                        switch (publishingPolicy) {
-                            case NONE:
-                                return (ExecutionPublishingFacet)new 
ExecutionPublishingFacetForPropertyAnnotationAsConfigured.None(holder);
-                            case ALL:
-                                return new 
ExecutionPublishingFacetForPropertyAnnotationAsConfigured.All(holder);
-                            default:
-                                throw new 
IllegalStateException(String.format("configured property.executionPublishing 
policy '%s' not recognised", publishingPolicy));
-                        }
-                    case DISABLED:
-                        return new 
ExecutionPublishingFacetForPropertyAnnotation.Disabled(holder);
-                    case ENABLED:
-                        return new 
ExecutionPublishingFacetForPropertyAnnotation.Enabled(holder);
-                    default:
-                        throw new 
IllegalStateException(String.format("@Property#executionPublishing '%s' not 
recognised", publishing));
-                }
-            })
+            .<ExecutionPublishingFacet>map(publishing -> (switch (publishing) {
+            case AS_CONFIGURED -> switch (publishingPolicy) {
+                case NONE -> new 
ExecutionPublishingFacetForPropertyAnnotationAsConfigured.None(holder);
+                case ALL -> new 
ExecutionPublishingFacetForPropertyAnnotationAsConfigured.All(holder);
+                default -> throw new IllegalStateException(
+                        String.format("configured property.executionPublishing 
policy '%s' not recognised", publishingPolicy));
+            };
+            case DISABLED -> new 
ExecutionPublishingFacetForPropertyAnnotation.Disabled(holder);
+            case ENABLED, ENABLED_FOR_UPDATES_ONLY -> new 
ExecutionPublishingFacetForPropertyAnnotation.Enabled(holder);
+            default -> throw new IllegalStateException(
+                    String.format("@Property#executionPublishing '%s' not 
recognised", publishing));
+            }))
             .orElseGet(() -> {
                 // there is no publishing facet from either @Action or 
@Property, so use the appropriate configuration to install a default
-                if (representsProperty(holder)) {
+                if (representsProperty(holder))
                     // we are dealing with a property
-                    switch (publishingPolicy) {
-                        case NONE:
-                            return new 
ExecutionPublishingFacetForPropertyFromConfiguration.None(holder);
-                        case ALL:
-                            return new 
ExecutionPublishingFacetForPropertyFromConfiguration.All(holder);
-                        default:
-                            throw new 
IllegalStateException(String.format("configured property.executionPublishing 
policy '%s' not recognised", publishingPolicy));
-                    }
-                } else {
+                    return switch (publishingPolicy) {
+                        case NONE -> new 
ExecutionPublishingFacetForPropertyFromConfiguration.None(holder);
+                        case ALL -> new 
ExecutionPublishingFacetForPropertyFromConfiguration.All(holder);
+                        default -> throw new IllegalStateException(
+                                String.format("configured 
property.executionPublishing policy '%s' not recognised", publishingPolicy));
+                    };
+                else {
                     // we are dealing with an action
                     var actionPublishingPolicy = 
ActionConfigOptions.actionExecutionPublishingPolicy(configuration);
-                    switch (actionPublishingPolicy) {
-                        case NONE:
-                            return new 
ExecutionPublishingFacetForActionFromConfiguration.None(holder);
-                        case IGNORE_QUERY_ONLY:
-                        case IGNORE_SAFE:
-                            return Facets.hasSafeSemantics(holder)
-                                    ? new 
ExecutionPublishingFacetForActionFromConfiguration.IgnoreSafe(holder)
-                                    : new 
ExecutionPublishingFacetForActionFromConfiguration.IgnoreSafeYetNot(holder);
-                        case ALL:
-                            return new 
ExecutionPublishingFacetForActionFromConfiguration.All(holder);
-                        default:
-                            throw new 
IllegalStateException(String.format("configured action.executionPublishing 
policy '%s' not recognised", actionPublishingPolicy));
-                    }
+                    return switch (actionPublishingPolicy) {
+                    case NONE -> new 
ExecutionPublishingFacetForActionFromConfiguration.None(holder);
+                    case IGNORE_QUERY_ONLY, IGNORE_SAFE -> 
Facets.hasSafeSemantics(holder)
+                                                        ? new 
ExecutionPublishingFacetForActionFromConfiguration.IgnoreSafe(holder)
+                                                        : new 
ExecutionPublishingFacetForActionFromConfiguration.IgnoreSafeYetNot(holder);
+                    case ALL -> new 
ExecutionPublishingFacetForActionFromConfiguration.All(holder);
+                    default -> throw new IllegalStateException(
+                            String.format("configured 
action.executionPublishing policy '%s' not recognised", 
actionPublishingPolicy));
+                    };
                 }
             });
     }
 
     private static boolean representsProperty(final FacetHolder holder) {
         // a property
-        if (holder instanceof TypedFacetHolder && 
((TypedFacetHolder)holder).featureType() == FeatureType.PROPERTY) {
+        if (holder instanceof TypedFacetHolder typedFacetHolder
+                && typedFacetHolder.featureType() == FeatureType.PROPERTY)
             return true;
-        }
         // or a mixin
-        return  holder.containsFacet(ContributingFacet.class) &&
-                holder.getFacet(ContributingFacet.class).contributed() == 
MixinFacet.Contributing.AS_PROPERTY;
+        return holder.lookupFacet(ContributingFacet.class)
+                .map(ContributingFacet::contributed)
+                .map(MixinFacet.Contributing.AS_PROPERTY::equals)
+                .orElse(false);
     }
 
     public ExecutionPublishingFacetForPropertyAnnotation(final FacetHolder 
holder) {
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetForDomainObjectAnnotation.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetForDomainObjectAnnotation.java
index 9956501d41c..56a686b2250 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetForDomainObjectAnnotation.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetForDomainObjectAnnotation.java
@@ -38,32 +38,27 @@ public static Optional<EntityChangePublishingFacet> create(
 
         var publish = 
entityChangePublishingIfAny.orElse(Publishing.AS_CONFIGURED);
 
-        switch (publish) {
-        case NOT_SPECIFIED:
-        case AS_CONFIGURED:
-
-            var publishingPolicy = 
DomainObjectConfigOptions.entityChangePublishingPolicy(configuration);
-            switch (publishingPolicy) {
-            case NONE:
-                return Optional.of(entityChangePublishingIfAny.isPresent()
+        return switch (publish) {
+            case NOT_SPECIFIED, AS_CONFIGURED -> {
+                var publishingPolicy = 
DomainObjectConfigOptions.entityChangePublishingPolicy(configuration);
+                yield switch (publishingPolicy) {
+                    case NONE -> 
Optional.of(entityChangePublishingIfAny.isPresent()
                         ? new 
EntityChangePublishingFacetForDomainObjectAnnotationAsConfigured(holder, false)
                         : new 
EntityChangePublishingFacetFromConfiguration(holder, false));
-            default:
-                return Optional.of(entityChangePublishingIfAny.isPresent()
+                    default -> 
Optional.of(entityChangePublishingIfAny.isPresent()
                         ? new 
EntityChangePublishingFacetForDomainObjectAnnotationAsConfigured(holder, true)
                         : new 
EntityChangePublishingFacetFromConfiguration(holder, true));
+                };
             }
-        case DISABLED:
-            return Optional.of(new 
EntityChangePublishingFacetForDomainObjectAnnotation(holder, false));
-        case ENABLED:
-            return Optional.of(new 
EntityChangePublishingFacetForDomainObjectAnnotation(holder, true));
-
-        default:
-            throw _Exceptions.unmatchedCase(publish);
-        }
+            case DISABLED -> Optional.of(new 
EntityChangePublishingFacetForDomainObjectAnnotation(holder, false, false, 
false, false));
+            case ENABLED -> Optional.of(new 
EntityChangePublishingFacetForDomainObjectAnnotation(holder, true, true, true, 
true));
+            case ENABLED_FOR_UPDATES_ONLY -> Optional.of(new 
EntityChangePublishingFacetForDomainObjectAnnotation(holder, true, false, true, 
false));
+            default -> throw _Exceptions.unmatchedCase(publish);
+        };
     }
 
-    protected EntityChangePublishingFacetForDomainObjectAnnotation(final 
FacetHolder holder, boolean enabled) {
-        super(holder, enabled);
+    protected EntityChangePublishingFacetForDomainObjectAnnotation(final 
FacetHolder holder, final boolean enabled,
+            final boolean enabledForCreate, final boolean enabledForUpdate, 
final boolean enabledForDelete) {
+        super(holder, enabled, enabledForCreate, enabledForUpdate, 
enabledForDelete);
     }
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetForDomainObjectAnnotationAsConfigured.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetForDomainObjectAnnotationAsConfigured.java
index df5890d9df7..e724d79135d 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetForDomainObjectAnnotationAsConfigured.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetForDomainObjectAnnotationAsConfigured.java
@@ -23,7 +23,7 @@
 public class EntityChangePublishingFacetForDomainObjectAnnotationAsConfigured 
extends EntityChangePublishingFacetForDomainObjectAnnotation {
 
     public 
EntityChangePublishingFacetForDomainObjectAnnotationAsConfigured(final 
FacetHolder facetHolder, final boolean enabled) {
-        super(facetHolder, enabled);
+        super(facetHolder, enabled, enabled, enabled, enabled);
     }
 
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetFromConfiguration.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetFromConfiguration.java
index cdeebda521c..0ef4aef74cf 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetFromConfiguration.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/domainobject/entitychangepublishing/EntityChangePublishingFacetFromConfiguration.java
@@ -30,7 +30,7 @@ public class EntityChangePublishingFacetFromConfiguration
 extends EntityChangePublishingFacetAbstract {
 
     public EntityChangePublishingFacetFromConfiguration(final FacetHolder 
facetHolder, final boolean enabled) {
-        super(facetHolder, enabled);
+        super(facetHolder, enabled, enabled, enabled, enabled);
     }
 
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/publish/entitychange/EntityChangePublishingFacet.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/publish/entitychange/EntityChangePublishingFacet.java
index 91513f3bee0..c8b869cdc12 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/publish/entitychange/EntityChangePublishingFacet.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/publish/entitychange/EntityChangePublishingFacet.java
@@ -18,6 +18,8 @@
  */
 package org.apache.causeway.core.metamodel.facets.object.publish.entitychange;
 
+import java.util.Optional;
+
 import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.facetapi.FacetHolder;
 import org.apache.causeway.core.metamodel.spec.ObjectSpecification;
@@ -30,22 +32,41 @@
  */
 public interface EntityChangePublishingFacet extends Facet {
 
-    public static boolean isPublishingEnabled(final FacetHolder facetHolder) {
-        if(facetHolder==null) {
-            return false;
-        }
+    static boolean isPublishingEnabled(final FacetHolder facetHolder) {
+        return entityChangePublishingFacet(facetHolder)
+                .map(EntityChangePublishingFacet::isEnabled)
+                .orElse(false);
+    }
+
+    static boolean isPublishingEnabledForCreate(final FacetHolder facetHolder) 
{
+        return entityChangePublishingFacet(facetHolder)
+                .map(EntityChangePublishingFacet::isEnabledForCreate)
+                .orElse(false);
+    }
 
-        if(facetHolder instanceof ObjectSpecification) {
-            if(!((ObjectSpecification)facetHolder).isEntity()) {
-                return false;
-            }
-        }
+    static boolean isPublishingEnabledForUpdate(final FacetHolder facetHolder) 
{
+        return entityChangePublishingFacet(facetHolder)
+                .map(EntityChangePublishingFacet::isEnabledForUpdate)
+                .orElse(false);
+    }
 
-        var entityChangePublishingFacet = 
facetHolder.getFacet(EntityChangePublishingFacet.class);
-        return entityChangePublishingFacet != null
-                && entityChangePublishingFacet.isEnabled();
+    static boolean isPublishingEnabledForDelete(final FacetHolder facetHolder) 
{
+        return entityChangePublishingFacet(facetHolder)
+                .map(EntityChangePublishingFacet::isEnabledForDelete)
+                .orElse(false);
     }
 
     boolean isEnabled();
+    boolean isEnabledForCreate();
+    boolean isEnabledForUpdate();
+    boolean isEnabledForDelete();
 
+    private static Optional<EntityChangePublishingFacet> 
entityChangePublishingFacet(final FacetHolder facetHolder) {
+        if(facetHolder==null)
+            return Optional.empty();
+        if(facetHolder instanceof ObjectSpecification objSpepc
+                && !objSpepc.isEntity())
+            return Optional.empty(); // optimization
+        return facetHolder.lookupFacet(EntityChangePublishingFacet.class);
+    }
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/publish/entitychange/EntityChangePublishingFacetAbstract.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/publish/entitychange/EntityChangePublishingFacetAbstract.java
index af60b3f0544..20ce78bf62f 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/publish/entitychange/EntityChangePublishingFacetAbstract.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/object/publish/entitychange/EntityChangePublishingFacetAbstract.java
@@ -32,12 +32,18 @@ private static final Class<? extends Facet> type() {
         return EntityChangePublishingFacet.class;
     }
 
-    @Getter
-    private final boolean enabled;
+    @Getter private final boolean enabled;
+    @Getter private final boolean enabledForCreate;
+    @Getter private final boolean enabledForUpdate;
+    @Getter private final boolean enabledForDelete;
 
-    public EntityChangePublishingFacetAbstract(final FacetHolder facetHolder, 
boolean enabled) {
+    public EntityChangePublishingFacetAbstract(final FacetHolder facetHolder, 
final boolean enabled,
+            final boolean enabledForCreate, final boolean enabledForUpdate, 
final boolean enabledForDelete) {
         super(EntityChangePublishingFacetAbstract.type(), facetHolder);
         this.enabled = enabled;
+        this.enabledForCreate = enabledForCreate;
+        this.enabledForUpdate = enabledForUpdate;
+        this.enabledForDelete = enabledForDelete;
     }
 
 }
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/property/entitychangepublishing/EntityPropertyChangePublishingPolicyFacet.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/property/entitychangepublishing/EntityPropertyChangePublishingPolicyFacet.java
index 6eca0d5a86a..4ea8c167ac0 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/property/entitychangepublishing/EntityPropertyChangePublishingPolicyFacet.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/property/entitychangepublishing/EntityPropertyChangePublishingPolicyFacet.java
@@ -18,14 +18,14 @@
  */
 package 
org.apache.causeway.core.metamodel.facets.properties.property.entitychangepublishing;
 
+import org.jspecify.annotations.NonNull;
+
 import org.apache.causeway.applib.annotation.Publishing;
 import org.apache.causeway.applib.value.Blob;
 import org.apache.causeway.applib.value.Clob;
 import org.apache.causeway.core.metamodel.facetapi.Facet;
 import org.apache.causeway.core.metamodel.spec.feature.OneToOneAssociation;
 
-import org.jspecify.annotations.NonNull;
-
 /**
  * Indicates whether a property should be excluded from entity change 
publishing (auditing).
  * @since 2.0
@@ -33,7 +33,7 @@
 public interface EntityPropertyChangePublishingPolicyFacet extends Facet {
 
     /**
-     * Must be one of Publishing.ENABLED or Publishing.DISABLED.
+     * Must be one of {@link Publishing#ENABLED}, {@link 
Publishing#ENABLED_FOR_UPDATES_ONLY} or {@link Publishing#DISABLED}.
      */
     @NonNull Publishing getEntityChangePublishing();
 
@@ -42,7 +42,8 @@ default boolean isPublishingVetoed() {
     }
 
     default boolean isPublishingAllowed() {
-        return getEntityChangePublishing() == Publishing.ENABLED;
+        return getEntityChangePublishing() == Publishing.ENABLED
+                || getEntityChangePublishing() == 
Publishing.ENABLED_FOR_UPDATES_ONLY;
     }
 
     static boolean isExcludedFromPublishing(final @NonNull OneToOneAssociation 
property) {
@@ -58,7 +59,7 @@ static boolean isExcludedFromPublishing(final @NonNull 
OneToOneAssociation prope
                     
.map(EntityPropertyChangePublishingPolicyFacet::isPublishingAllowed)
                     .orElse(false);
 
-            //XXX CAUSEWAY-1488, exclude Bob/Clob from property change 
publishing unless explicitly allowed
+            //XXX CAUSEWAY-1488, exclude Blob/Clob from property change 
publishing unless explicitly allowed
             return !isExplictlyAllowed;
         }
 
diff --git 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/property/entitychangepublishing/EntityPropertyChangePublishingPolicyFacetForPropertyAnnotation.java
 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/property/entitychangepublishing/EntityPropertyChangePublishingPolicyFacetForPropertyAnnotation.java
index dbf4cc19abc..bcba7479eb2 100644
--- 
a/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/property/entitychangepublishing/EntityPropertyChangePublishingPolicyFacetForPropertyAnnotation.java
+++ 
b/core/metamodel/src/main/java/org/apache/causeway/core/metamodel/facets/properties/property/entitychangepublishing/EntityPropertyChangePublishingPolicyFacetForPropertyAnnotation.java
@@ -34,9 +34,9 @@ public static 
Optional<EntityPropertyChangePublishingPolicyFacet> create(
         return propertyIfAny
                 .map(Property::entityChangePublishing)
                 // only install facet if policy is explicit ('enabled' or 
'disabled')
-                .filter(entityChangePublishing ->
-                    entityChangePublishing == Publishing.ENABLED
-                        || entityChangePublishing == Publishing.DISABLED)
+                .filter(entityChangePublishing -> entityChangePublishing == 
Publishing.ENABLED
+                    || entityChangePublishing == 
Publishing.ENABLED_FOR_UPDATES_ONLY
+                    || entityChangePublishing == Publishing.DISABLED)
                 .map(entityChangePublishing ->
                     new 
EntityPropertyChangePublishingPolicyFacetForPropertyAnnotation(entityChangePublishing,
 holder));
     }
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
index 6e284489ccb..a286ff984aa 100644
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
+++ 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/FacetFactoryTestAbstract.java
@@ -120,8 +120,7 @@ public static CollectionScenarioBuilder builder(final 
Class<?> declaringClass, f
 
     // -- SETUP
 
-    @Getter(onMethod_ = {@Override})
-    private MetaModelContext metaModelContext;
+    @Getter(onMethod_ = {@Override}) protected MetaModelContext 
metaModelContext;
 
     private MethodRemover_forTesting methodRemover;
 
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactoryTest.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactoryTest.java
index e6fb9db01d6..deb67cba2de 100644
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactoryTest.java
+++ 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/object/domainobject/DomainObjectAnnotationFacetFactoryTest.java
@@ -24,6 +24,7 @@
 
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Nested;
 import org.junit.jupiter.api.Test;
 
 import static org.hamcrest.CoreMatchers.is;
@@ -39,12 +40,12 @@
 import org.apache.causeway.applib.annotation.Bounding;
 import org.apache.causeway.applib.annotation.DomainObject;
 import org.apache.causeway.applib.annotation.DomainService;
+import org.apache.causeway.applib.annotation.Publishing;
 import org.apache.causeway.applib.id.LogicalType;
 import org.apache.causeway.applib.mixins.system.HasInteractionId;
 import org.apache.causeway.commons.collections.Can;
 import 
org.apache.causeway.core.config.metamodel.facets.DomainObjectConfigOptions;
 import org.apache.causeway.core.metamodel.facetapi.Facet;
-import 
org.apache.causeway.core.metamodel.facets.AbstractTestWithMetaModelContext;
 import org.apache.causeway.core.metamodel.facets.FacetFactoryTestAbstract;
 import 
org.apache.causeway.core.metamodel.facets.object.autocomplete.AutoCompleteFacet;
 import 
org.apache.causeway.core.metamodel.facets.object.domainobject.autocomplete.AutoCompleteFacetForDomainObjectAnnotation;
@@ -74,7 +75,7 @@ protected void tearDown() throws Exception {
         facetFactory = null;
     }
 
-    class Customer {
+    static class Customer {
     }
 
     class SomeHasInteractionId implements HasInteractionId {
@@ -110,7 +111,8 @@ protected void ignoringConfiguration() {
         facetFactory = new 
DomainObjectAnnotationFacetFactory(getMetaModelContext());
     }
 
-    public static class EntityChangePublishing extends 
DomainObjectAnnotationFacetFactoryTest {
+    @Nested
+    public class EntityChangePublishing  {
 
         @DomainObject(entityChangePublishing = 
org.apache.causeway.applib.annotation.Publishing.AS_CONFIGURED)
         class CustomerWithDomainObjectAndAuditingSetToAsConfigured {
@@ -124,7 +126,12 @@ class CustomerWithDomainObjectAndAuditingSetToDisabled {
         class CustomerWithDomainObjectAndAuditingSetToEnabled {
         }
 
-        public static class WhenNotAnnotatedAndDefaultsFromConfiguration 
extends EntityChangePublishing {
+        @DomainObject(entityChangePublishing = 
Publishing.ENABLED_FOR_UPDATES_ONLY)
+        class 
CustomerWithDomainObjectAndEntityChangePublishingSetToEnabledForUpdatesOnly {
+        }
+
+        @Nested
+        public class WhenNotAnnotatedAndDefaultsFromConfiguration {
 
             @Test
             void configured_value_set_to_all() {
@@ -158,7 +165,8 @@ void configured_value_set_to_none() {
 
         }
 
-        public static class 
WithDomainObjectAnnotationWithAuditingSetToAsConfigured extends 
EntityChangePublishing {
+        @Nested
+        public class WithDomainObjectAnnotationWithAuditingSetToAsConfigured {
 
             @Test
             public void configured_value_set_to_all() {
@@ -191,7 +199,8 @@ public void configured_value_set_to_none() {
 
         }
 
-        public static class WithDomainObjectAnnotationWithAuditingSetToEnabled 
extends EntityChangePublishing {
+        @Nested
+        public class WithDomainObjectAnnotationWithAuditingSetToEnabled {
 
             @Test
             public void irrespective_of_configured_value() {
@@ -209,7 +218,31 @@ public void irrespective_of_configured_value() {
 
         }
 
-        public static class 
WithDomainObjectAnnotationWithAuditingSetToDisabled extends 
EntityChangePublishing {
+        @Nested
+        public class 
WithDomainObjectAnnotationWithEntityChangePublishingSetToEnabledForUpdatesOnly {
+
+            @Test
+            public void irrespective_of_configured_value() {
+                allowingEntityChangePublishingToReturn(null);
+                
objectScenario(CustomerWithDomainObjectAndEntityChangePublishingSetToEnabledForUpdatesOnly.class,
 (processClassContext, facetHolder)->{
+                    facetFactory.process(processClassContext);
+
+                    final Facet facet = 
facetHolder.getFacet(EntityChangePublishingFacet.class);
+                    assertNotNull(facet);
+                    assertTrue(facet instanceof 
EntityChangePublishingFacetForDomainObjectAnnotation);
+
+                    
assertFalse(EntityChangePublishingFacet.isPublishingEnabledForCreate(facetHolder));
+                    
assertTrue(EntityChangePublishingFacet.isPublishingEnabledForUpdate(facetHolder));
+                    
assertFalse(EntityChangePublishingFacet.isPublishingEnabledForDelete(facetHolder));
+
+                    assertNoMethodsRemoved();
+                });
+            }
+
+        }
+
+        @Nested
+        public class WithDomainObjectAnnotationWithAuditingSetToDisabled {
 
             @Test
             public void irrespective_of_configured_value() {
@@ -226,7 +259,8 @@ public void irrespective_of_configured_value() {
         }
     }
 
-    public static class AutoComplete extends 
DomainObjectAnnotationFacetFactoryTest {
+    @Nested
+    public class AutoComplete {
 
         class CustomerRepository {
             public String lookup(final String x) { return null; }
@@ -322,7 +356,8 @@ public void whenNoDomainObjectAnnotation() {
 
     }
 
-    public static class Bounded extends DomainObjectAnnotationFacetFactoryTest 
{
+    @Nested
+    public class Bounded {
 
         @DomainObject(bounding = Bounding.BOUNDED)
         class CustomerWithDomainObjectAndBoundedSetToTrue {
@@ -384,24 +419,24 @@ public void whenNoDomainObjectAnnotation() {
         }
     }
 
-    public static class Editing extends DomainObjectAnnotationFacetFactoryTest 
{
 
-        class CustomerWithImmutableAnnotation {
-        }
+    @DomainObject(editing = 
org.apache.causeway.applib.annotation.Editing.AS_CONFIGURED)
+    static class CustomerWithDomainObjectAndEditingSetToAsConfigured {
+    }
 
-        @DomainObject(editing = 
org.apache.causeway.applib.annotation.Editing.AS_CONFIGURED)
-        class CustomerWithDomainObjectAndEditingSetToAsConfigured {
-        }
+    @DomainObject(editing = 
org.apache.causeway.applib.annotation.Editing.DISABLED)
+    static class CustomerWithDomainObjectAndEditingSetToDisabled {
+    }
 
-        @DomainObject(editing = 
org.apache.causeway.applib.annotation.Editing.DISABLED)
-        class CustomerWithDomainObjectAndEditingSetToDisabled {
-        }
+    @DomainObject(editing = 
org.apache.causeway.applib.annotation.Editing.ENABLED)
+    static class CustomerWithDomainObjectAndEditingSetToEnabled {
+    }
 
-        @DomainObject(editing = 
org.apache.causeway.applib.annotation.Editing.ENABLED)
-        class CustomerWithDomainObjectAndEditingSetToEnabled {
-        }
+    @Nested
+    public class Editing {
 
-        public static class WhenNotAnnotatedAndDefaultsFromConfiguration 
extends Editing {
+        @Nested
+        public class WhenNotAnnotatedAndDefaultsFromConfiguration {
 
             @Test
             public void configured_value_set_to_true() {
@@ -448,7 +483,8 @@ public void configured_value_set_to_defaults() {
             }
         }
 
-        public static class 
WithDomainObjectAnnotationWithEditingSetToAsConfigured extends Editing {
+        @Nested
+        public class WithDomainObjectAnnotationWithEditingSetToAsConfigured {
 
             @Test
             public void configured_value_set_to_true() {
@@ -492,7 +528,8 @@ public void configured_value_set_to_defaults() {
             }
         }
 
-        public static class WithDomainObjectAnnotationWithEditingSetToEnabled 
extends Editing {
+        @Nested
+        public class WithDomainObjectAnnotationWithEditingSetToEnabled {
 
             @Test
             public void irrespective_of_configured_value() {
@@ -508,7 +545,8 @@ public void irrespective_of_configured_value() {
             }
         }
 
-        public static class WithDomainObjectAnnotationWithEditingSetToDisabled 
extends Editing {
+        @Nested
+        public class WithDomainObjectAnnotationWithEditingSetToDisabled {
 
             @Test
             public void irrespective_of_configured_value() {
@@ -526,16 +564,17 @@ public void irrespective_of_configured_value() {
         }
     }
 
-    public static class LogicalTypeName extends 
DomainObjectAnnotationFacetFactoryTest {
+    @Named("CUS")
+    @DomainObject
+    static class LogicalTypeNameCustomerWithDomainObjectAndObjectTypeSet {
+    }
 
-        @Named("CUS")
-        @DomainObject
-        class CustomerWithDomainObjectAndObjectTypeSet {
-        }
+    @DomainObject
+    static class LogicalTypeNameCustomerWithDomainObjectButNoObjectType {
+    }
 
-        @DomainObject
-        class CustomerWithDomainObjectButNoObjectType {
-        }
+    @Nested
+    public class LogicalTypeName {
 
         @BeforeEach
         public void setUp() {
@@ -544,7 +583,7 @@ public void setUp() {
 
         @Test
         public void whenDomainObjectAndObjectTypeSetToTrue() {
-            
assertThat(LogicalType.infer(CustomerWithDomainObjectAndObjectTypeSet.class).logicalName(),
+            
assertThat(LogicalType.infer(LogicalTypeNameCustomerWithDomainObjectAndObjectTypeSet.class).logicalName(),
                     is("CUS"));
             assertNoMethodsRemoved();
         }
@@ -552,7 +591,7 @@ public void whenDomainObjectAndObjectTypeSetToTrue() {
         @Test
         public void whenDomainObjectAndObjectTypeNotSet() {
 
-            objectScenario(CustomerWithDomainObjectButNoObjectType.class, 
(processClassContext, facetHolder)->{
+            
objectScenario(LogicalTypeNameCustomerWithDomainObjectButNoObjectType.class, 
(processClassContext, facetHolder)->{
                 facetFactory.process(processClassContext);
 
                 final Facet facet = facetHolder.getFacet(AliasedFacet.class);
@@ -577,23 +616,24 @@ public void whenNoDomainObjectAnnotation() {
 
     }
 
-    public static class Nature extends DomainObjectAnnotationFacetFactoryTest {
+    @DomainObject(nature = org.apache.causeway.applib.annotation.Nature.ENTITY)
+    static class CustomerWithDomainObjectAndNatureSetToJdoEntity {
+    }
 
-        @DomainObject(nature = 
org.apache.causeway.applib.annotation.Nature.ENTITY)
-        class CustomerWithDomainObjectAndNatureSetToJdoEntity {
-        }
+    @DomainObject(nature = 
org.apache.causeway.applib.annotation.Nature.NOT_SPECIFIED)
+    static class CustomerWithDomainObjectAndNatureSetToNotSpecified {
+    }
 
-        @DomainObject(nature = 
org.apache.causeway.applib.annotation.Nature.NOT_SPECIFIED)
-        class CustomerWithDomainObjectAndNatureSetToNotSpecified {
-        }
+    @DomainObject(nature = 
org.apache.causeway.applib.annotation.Nature.VIEW_MODEL)
+    static class CustomerWithDomainObjectAndNatureSetToViewModel {
+    }
 
-        @DomainObject(nature = 
org.apache.causeway.applib.annotation.Nature.VIEW_MODEL)
-        class CustomerWithDomainObjectAndNatureSetToViewModel {
-        }
+    @DomainObject
+    static class CustomerWithDomainObjectButNoNature {
+    }
 
-        @DomainObject
-        class CustomerWithDomainObjectButNoNature {
-        }
+    @Nested
+    public class Nature  {
 
         @BeforeEach
         public void setUp() {
@@ -658,18 +698,19 @@ public void whenNoDomainObjectAnnotation() {
 
     }
 
-    public static class Alias extends AbstractTestWithMetaModelContext {
-        DomainObjectAnnotationFacetFactory facetFactory;
+    @Named("object.name")
+    @DomainObject(aliased = {"object.name", "object.alias"})
+    static class AliasDomainObjectWithAliases {
+    }
 
-        @Named("object.name")
-        @DomainObject(aliased = {"object.name", "object.alias"})
-        class DomainObjectWithAliases {
-        }
+    @Named("service.name")
+    @DomainService(aliased = {"service.name", "service.alias"})
+    static class AliasDomainServiceWithAliases {
+    }
 
-        @Named("service.name")
-        @DomainService(aliased = {"service.name", "service.alias"})
-        class DomainServiceWithAliases {
-        }
+    @Nested
+    public class Alias {
+        DomainObjectAnnotationFacetFactory facetFactory;
 
         @Test
         public void testValidationDomainObjectWithAliasesConfigured() {
@@ -680,7 +721,7 @@ public void 
testValidationDomainObjectWithAliasesConfigured() {
             facetFactory = new 
DomainObjectAnnotationFacetFactory(getMetaModelContext());
             ((MetaModelContext_forTesting) 
getMetaModelContext()).getProgrammingModel();//kicks off the programming model 
factory
 
-            
getMetaModelContext().getSpecificationLoader().specForTypeElseFail(DomainObjectWithAliases.class);
+            
getMetaModelContext().getSpecificationLoader().specForTypeElseFail(AliasDomainObjectWithAliases.class);
             ValidationFailures validationFailures = 
getMetaModelContext().getSpecificationLoader().getOrAssessValidationResult();
             assertFalse(validationFailures.hasFailures());
         }
@@ -694,7 +735,7 @@ public void 
testValidationDomainServiceWithAliasesConfigured() {
             facetFactory = new 
DomainObjectAnnotationFacetFactory(getMetaModelContext());
             ((MetaModelContext_forTesting) 
getMetaModelContext()).getProgrammingModel();//kicks off the programming model 
factory
 
-            
getMetaModelContext().getSpecificationLoader().specForTypeElseFail(DomainServiceWithAliases.class);
+            
getMetaModelContext().getSpecificationLoader().specForTypeElseFail(AliasDomainServiceWithAliases.class);
             ValidationFailures validationFailures = 
getMetaModelContext().getSpecificationLoader().getOrAssessValidationResult();
             assertFalse(validationFailures.hasFailures());
         }
@@ -706,7 +747,7 @@ public void testValidationDomainObjectWithAliasesDefault() {
             facetFactory = new 
DomainObjectAnnotationFacetFactory(getMetaModelContext());
             ((MetaModelContext_forTesting) 
getMetaModelContext()).getProgrammingModel();//kicks off the programming model 
factory
 
-            
getMetaModelContext().getSpecificationLoader().specForTypeElseFail(DomainObjectWithAliases.class);
+            
getMetaModelContext().getSpecificationLoader().specForTypeElseFail(AliasDomainObjectWithAliases.class);
             ValidationFailures validationFailures = 
getMetaModelContext().getSpecificationLoader().getOrAssessValidationResult();
             assertTrue(validationFailures.hasFailures());
         }
@@ -719,7 +760,7 @@ public void testValidationDomainServiceWithAliasesDefault() 
{
             facetFactory = new 
DomainObjectAnnotationFacetFactory(getMetaModelContext());
             ((MetaModelContext_forTesting) 
getMetaModelContext()).getProgrammingModel();//kicks off the programming model 
factory
 
-            
getMetaModelContext().getSpecificationLoader().specForTypeElseFail(DomainServiceWithAliases.class);
+            
getMetaModelContext().getSpecificationLoader().specForTypeElseFail(AliasDomainServiceWithAliases.class);
             ValidationFailures validationFailures = 
getMetaModelContext().getSpecificationLoader().getOrAssessValidationResult();
             assertTrue(validationFailures.hasFailures());
         }
diff --git 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java
 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java
index e4a67edb234..17bbf6dab49 100644
--- 
a/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java
+++ 
b/core/mmtest/src/test/java/org/apache/causeway/core/metamodel/facets/properties/property/PropertyAnnotationFacetFactoryTest.java
@@ -193,9 +193,8 @@ private void assertHasPropertyDomainEventFacet(
             assertEquals(eventTypeOrigin, 
domainEventFacet.getEventTypeOrigin());
             assertThat(domainEventFacet.getEventType(), 
CausewayMatchers.classEqualTo(eventType));
 
-            if(facetedMethod.methodFacade().getName().equals("prop")) {
+            if(facetedMethod.methodFacade().getName().equals("prop"))
                 return; // skip further checks, when in a mixed-in scenario
-            }
 
             // then
             var setterFacet = 
facetedMethod.getFacet(PropertySetterFacet.class);
@@ -669,7 +668,7 @@ class Customer {
                 // when
                 processEntityPropertyChangePublishing(facetFactory, 
processMethodContext);
                 // then
-                var changePolicyFacet = 
facetedMethod.getFacet(EntityPropertyChangePublishingPolicyFacet.class);
+                var changePolicyFacet = 
facetedMethod.lookupFacet(EntityPropertyChangePublishingPolicyFacet.class).orElse(null);
                 assertNotNull(changePolicyFacet);
                 assertTrue(changePolicyFacet.isPublishingVetoed());
                 assertFalse(changePolicyFacet.isPublishingAllowed());
diff --git 
a/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/changetracking/EntityChangeTrackerDefault.java
 
b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/changetracking/EntityChangeTrackerDefault.java
index 42ed58076e4..78f8cc9d4d1 100644
--- 
a/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/changetracking/EntityChangeTrackerDefault.java
+++ 
b/persistence/commons/src/main/java/org/apache/causeway/persistence/commons/integration/changetracking/EntityChangeTrackerDefault.java
@@ -39,18 +39,12 @@
 import jakarta.inject.Named;
 import jakarta.inject.Provider;
 
-import org.apache.causeway.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
-import org.apache.causeway.core.metamodel.execution.InteractionInternal;
-
-import org.apache.causeway.core.metamodel.services.deadlock.DeadlockRecognizer;
-import org.apache.causeway.schema.chg.v2.ChangesDto;
-import org.apache.causeway.schema.chg.v2.ObjectsDto;
-import org.apache.causeway.schema.common.v2.OidsDto;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.Nullable;
 
 import org.springframework.beans.factory.annotation.Qualifier;
 import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.core.Ordered;
-import org.jspecify.annotations.Nullable;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.support.TransactionSynchronization;
@@ -61,6 +55,7 @@
 import org.apache.causeway.applib.annotation.PriorityPrecedence;
 import org.apache.causeway.applib.annotation.Programmatic;
 import org.apache.causeway.applib.annotation.TransactionScope;
+import org.apache.causeway.applib.jaxb.JavaSqlXMLGregorianCalendarMarshalling;
 import org.apache.causeway.applib.services.bookmark.Bookmark;
 import org.apache.causeway.applib.services.iactn.Interaction;
 import org.apache.causeway.applib.services.iactn.InteractionProvider;
@@ -74,10 +69,12 @@
 import org.apache.causeway.commons.internal.collections._Sets;
 import org.apache.causeway.commons.internal.exceptions._Exceptions;
 import org.apache.causeway.core.config.CausewayConfiguration;
+import org.apache.causeway.core.metamodel.execution.InteractionInternal;
 import 
org.apache.causeway.core.metamodel.facets.object.publish.entitychange.EntityChangePublishingFacet;
 import org.apache.causeway.core.metamodel.object.ManagedObject;
 import org.apache.causeway.core.metamodel.object.ManagedObjects;
 import org.apache.causeway.core.metamodel.object.MmEntityUtils;
+import org.apache.causeway.core.metamodel.services.deadlock.DeadlockRecognizer;
 import 
org.apache.causeway.core.metamodel.services.objectlifecycle.HasEnlistedEntityPropertyChanges;
 import 
org.apache.causeway.core.metamodel.services.objectlifecycle.PreAndPostValue;
 import 
org.apache.causeway.core.metamodel.services.objectlifecycle.PropertyChangeRecord;
@@ -88,9 +85,11 @@
 import 
org.apache.causeway.core.transaction.changetracking.EntityPropertyChangePublisher;
 import 
org.apache.causeway.core.transaction.changetracking.HasEnlistedEntityChanges;
 import 
org.apache.causeway.persistence.commons.CausewayModulePersistenceCommons;
+import org.apache.causeway.schema.chg.v2.ChangesDto;
+import org.apache.causeway.schema.chg.v2.ObjectsDto;
+import org.apache.causeway.schema.common.v2.OidsDto;
 
 import lombok.Getter;
-import org.jspecify.annotations.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 
@@ -305,21 +304,18 @@ private boolean shouldPublish(final PreAndPostValue 
preAndPostValue) {
 
     private boolean isEntityExcludedForChangePublishing(final ManagedObject 
entity) {
 
-        if (!configuration.isEnabled()) {
+        if (!configuration.isEnabled())
             return true;
-        }
 
-        if(!EntityChangePublishingFacet.isPublishingEnabled(entity.objSpec())) 
{
+        if(!EntityChangePublishingFacet.isPublishingEnabled(entity.objSpec()))
             return true; // ignore entities that are not enabled for entity 
change publishing
-        }
 
         // guard against transient
         if(ManagedObjects.bookmark(entity).isEmpty()) return true;
 
-        if(changes.isMemoized()) {
+        if(changes.isMemoized())
             throw _Exceptions.illegalState("Cannot enlist additional changes 
for auditing, "
                     + "since changedObjectPropertiesRef was already prepared 
(memoized) for auditing.");
-        }
 
         return false;
     }
@@ -373,9 +369,8 @@ public Optional<EntityChanges> getEntityChanges(
 
         // a defensive copy of
         var changeKindByEnlistedAdapter = new 
HashMap<>(this.changeKindByEnlistedAdapter);
-        if(changeKindByEnlistedAdapter.isEmpty()) {
+        if(changeKindByEnlistedAdapter.isEmpty())
             return Optional.empty();
-        }
 
         final Interaction interaction = currentInteraction();
         final int numberEntitiesLoaded1 = numberEntitiesLoaded();
@@ -418,9 +413,8 @@ private static ChangesDto newDto(
 
         changeKindByEnlistedEntity.forEach((bookmark, kind)->{
             var oidDto = bookmark.toOidDto();
-            if(oidDto==null) {
+            if(oidDto==null)
                 return;
-            }
             switch(kind) {
                 case CREATE:
                     objectsDto.getCreated().getOid().add(oidDto);
@@ -530,9 +524,9 @@ public void enlistCreated(final ManagedObject entity) {
 
         _Xray.enlistCreated(entity, interactionProviderProvider);
 
-        if (isEntityExcludedForChangePublishing(entity)) {
+        if (isEntityExcludedForChangePublishing(entity)
+                || 
!EntityChangePublishingFacet.isPublishingEnabledForCreate(entity.objSpec()))
             return;
-        }
 
         log.debug("enlist entity's property changes for publishing {}", 
entity);
 
@@ -551,9 +545,9 @@ public void enlistUpdating(
 
         _Xray.enlistUpdating(entity, interactionProviderProvider);
 
-        if (isEntityExcludedForChangePublishing(entity)) {
+        if (isEntityExcludedForChangePublishing(entity)
+                || 
!EntityChangePublishingFacet.isPublishingEnabledForUpdate(entity.objSpec()))
             return;
-        }
 
         if(log.isDebugEnabled()) {
             log.debug("enlist entity's property changes for publishing {}", 
entity);
@@ -588,7 +582,9 @@ public void enlistDeleting(final ManagedObject entity) {
 
         _Xray.enlistDeleting(entity, interactionProviderProvider);
 
-        if (isEntityExcludedForChangePublishing(entity)) return;
+        if (isEntityExcludedForChangePublishing(entity)
+                || 
!EntityChangePublishingFacet.isPublishingEnabledForDelete(entity.objSpec()))
+            return;
 
         suppressAutoFlushIfRequired(() -> {
             final boolean enlisted = enlistForChangeKindPublishing(entity, 
EntityChangeKind.DELETE);

Reply via email to