infer type as a subworkflow step if `step` is omitted but `steps` are supplied
diff --git a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExecutionContext.java b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExecutionContext.java
index f154d92..302ef61 100644
--- a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExecutionContext.java
+++ b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExecutionContext.java
@@ -557,7 +557,8 @@
                     }
                     log.debug("Request to replay resuming " + WorkflowExecutionContext.this + " at non-idempotent step; rolling back to " + replayableLastStep);
                     if (replayableLastStep == null) {
-                        throw new IllegalArgumentException("Cannot replay resuming as there are no replay points and last step " + currentStepIndex + " is not idempotent");
+                        throw new IllegalArgumentException("Cannot replay resuming as there are no replay points and last step " + currentStepIndex + " is not idempotent; " +
+                                "should that step or a previous one declare 'idempotent: true' or 'replayable: from here' ?");
                     }
                     return makeInstructionsForReplayingFromStep(replayableLastStep, reason, false);
                 }
diff --git a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowStepResolution.java b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowStepResolution.java
index 2d17c6c..e127e78 100644
--- a/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowStepResolution.java
+++ b/core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowStepResolution.java
@@ -97,19 +97,21 @@
                 Object s = defM.remove("step");
                 if (s == null) s = defM.remove("shorthand");
                 if (s == null) s = defM.remove("s");
-                if (s == null) {
-                    if (defM.size()==1) {
-                        // assume the colon caused it accidentally to be a map
-                        s = Iterables.getOnlyElement(defM.keySet());
-                        if (s instanceof String && ((String)s).contains(" ")) {
-                            s = s + " : " + Iterables.getOnlyElement(defM.values());
-                        } else {
-                            s = null;
-                        }
+                if (s==null && defM.containsKey("steps")) {
+                    // if it has steps, but no step or s, assume it is a subworkflow
+                    s = "subworkflow";
+                }
+                if (s == null && defM.size()==1) {
+                    // assume the colon caused it accidentally to be a map
+                    s = Iterables.getOnlyElement(defM.keySet());
+                    if (s instanceof String && ((String)s).contains(" ")) {
+                        s = s + " : " + Iterables.getOnlyElement(defM.values());
+                    } else {
+                        s = null;
                     }
-                    if (s==null) {
-                        throw new IllegalArgumentException("Step definition must indicate a `type` or a `step` / `shorthand` / `s` (" + def + ")");
-                    }
+                }
+                if (s==null) {
+                    throw new IllegalArgumentException("Step definition must indicate a `type` or a `step` / `shorthand` / `s` (" + def + ")");
                 }
                 if (!(s instanceof String)) {
                     throw new IllegalArgumentException("step shorthand must be a string");
diff --git a/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowSubAndCustomExtensionEdgeTest.java b/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowSubAndCustomExtensionEdgeTest.java
index dc6fe61..6431170 100644
--- a/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowSubAndCustomExtensionEdgeTest.java
+++ b/core/src/test/java/org/apache/brooklyn/core/workflow/WorkflowSubAndCustomExtensionEdgeTest.java
@@ -143,18 +143,31 @@
 
     @Test
     public void testSubWorkflowStep() throws Exception {
+        MutableList<String> steps = MutableList.of(
+                "let v0 = ${v0}B",
+                "let v1 = ${v1}B",
+                "let v3 = V3B",
+                "return done");
         runWorkflow(MutableList.of(
                 "let v1 = V1",
                 "let v2 = V2",
-                MutableMap.of("step", "subworkflow",
-                    "steps", MutableList.of(
-                            "let v0 = ${v0}B",
-                            "let v1 = ${v1}B",
-                            "let v3 = V3B",
-                            "return done")),
+                MutableMap.of(
+                        "step", "subworkflow",
+                    "steps", steps),
                 "return ${v0}-${v1}-${v2}-${v3}-${output}"),
                 ConfigBag.newInstance().configure(WorkflowCommonConfig.INPUT, MutableMap.of("v0", "V0")) );
         Asserts.assertEquals(lastInvocation.getUnchecked(), "V0B-V1B-V2-V3B-done");
+
+        // subworkflow is chosen implicitly if step is omitted
+        runWorkflow(MutableList.of(
+                        "let v1 = V1",
+                        "let v2 = V2",
+                        MutableMap.of(
+                                //"step", "subworkflow",
+                                "steps", steps),
+                        "return ${v0}-${v1}-${v2}-${v3}-${output}"),
+                ConfigBag.newInstance().configure(WorkflowCommonConfig.INPUT, MutableMap.of("v0", "V0")) );
+        Asserts.assertEquals(lastInvocation.getUnchecked(), "V0B-V1B-V2-V3B-done");
     }
 
 }