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

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


The following commit(s) were added to refs/heads/main by this push:
     new 38fac5360201 CAMEL-24745: the canonical YAML DSL schema requires 
route: a top-level from: is the compact notation, reported with the route form
38fac5360201 is described below

commit 38fac5360201051acb916b1fd8ad692ad8a69c8b
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Sep 20 20:17:31 2026 +0200

    CAMEL-24745: the canonical YAML DSL schema requires route: a top-level 
from: is the compact notation, reported with the route form
    
    camel run warned on a top-level - from: without - route: as the compact
    notation, while camel validate yaml --canonical accepted it because the
    canonical schema listed from among its top-level entries. The two now agree,
    on the side of the runtime: the canonical form requires route:. XML routes
    have always been <route> with <from> inside, the YAML DSL follows the XML
    shape, and Kaoto and Karavan write the route form; the Java DSL's bare 
from()
    is a convenience of the RouteBuilder, not a document shape.
    
    The schema generator leaves the compact @YamlIn type out of the canonical
    schema, so camelYamlDsl-canonical.json has no top-level from and no orphan
    definition for it. The validator reports a top-level from: with the form to
    write, the way it reports the other compact shapes. The YAML DSL doc's
    examples, the Integration template of camel init and the affected tests are
    written in the route form; a Kamelet's template: keeps its from:, which is
    the Kamelet spec's shape. Upgrade guide note added.
    
    Closes #26632
    
    Co-Authored-By: Claude Fable 5.1 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01Bp3538HRBPMQkb5ta9xRaj
---
 .../org/apache/camel/catalog/docs/yaml-dsl.adoc    | 141 +++++++++++----------
 .../camel/catalog/schemas/camelYamlDsl-model.json  | 110 ++++++++--------
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    |  10 ++
 .../main/resources/templates/integration.yaml.ftl  |  23 ++--
 .../dsl/jbang/core/common/TemplateHelperTest.java  |   5 +-
 .../maven/dsl/yaml/GenerateYamlSchemaMojo.java     |   8 ++
 .../camel/dsl/yaml/validator/SchemaHints.java      |   6 +
 .../yaml/validator/YamlCanonicalValidatorTest.java |  18 +++
 .../validator/YamlValidatorExpressionHintTest.java |  25 ++--
 .../validator/YamlValidatorScalarLeniencyTest.java |  42 +++---
 .../resources/schema/camelYamlDsl-canonical.json   |  34 -----
 .../resources/schema/camelYamlDsl-model.json       | 110 ++++++++--------
 .../camel-yaml-dsl/src/main/docs/yaml-dsl.adoc     | 141 +++++++++++----------
 .../camel/dsl/yaml/YamlCompletionTreeTest.java     |   4 +-
 14 files changed, 348 insertions(+), 329 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
index c1ad1cd0126c..0f9c094dcc45 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/yaml-dsl.adoc
@@ -19,20 +19,21 @@ A route is collection of elements defined as follows:
 
 [source,yaml]
 ----
-- from: #<1>
-    uri: "direct:start"
-    steps: #<2>
-      - filter:
-          expression:
-            simple: "${in.header.continue} == true"
-          steps: #<2>
-            - to:
-                uri: "log:filtered"
-      - to:
-          uri: "log:original"
+- route: #<1>
+    from:
+      uri: "direct:start"
+      steps: #<2>
+        - filter:
+            expression:
+              simple: "${in.header.continue} == true"
+            steps: #<2>
+              - to:
+                  uri: "log:filtered"
+        - to:
+            uri: "log:original"
 ----
 
-<1> route entry point, by default `from` and `rest` are supported
+<1> a route, with the `from` endpoint it consumes from (`rest`, `beans`, 
`onException` and more are the other top-level entries)
 <2> processing steps
 
 [NOTE]
@@ -190,14 +191,15 @@ This allows YAML such as:
 
 [source,yaml]
 ----
-- from:
-    uri: "direct:start"
-    steps:
-      - myStep:
-          id: "custom-step"
-          steps:
-            - to:
-                uri: "mock:result"
+- route:
+    from:
+      uri: "direct:start"
+      steps:
+        - myStep:
+            id: "custom-step"
+            steps:
+              - to:
+                  uri: "mock:result"
 ----
 
 Resolvers are discovered when YAML routes are parsed. Camel starts with the 
built-in YAML DSL resolvers, then adds the
@@ -295,25 +297,27 @@ To define an endpoint with the YAML dsl you have two 
options:
 +
 [source,yaml]
 ----
-- from:
-    uri: "timer:tick?period=1s"
-    steps:
-      - to:
-          uri: "telegram:bots?authorizationToken=XXX"
+- route:
+    from:
+      uri: "timer:tick?period=1s"
+      steps:
+        - to:
+            uri: "telegram:bots?authorizationToken=XXX"
 ----
 . Using URI and parameters:
 +
 [source,yaml]
 ----
-- from:
-    uri: "timer://tick"
-    parameters:
-      period: "1s"
-    steps:
-      - to:
-          uri: "telegram:bots"
-          parameters:
-            authorizationToken: "XXX"
+- route:
+    from:
+      uri: "timer://tick"
+      parameters:
+        period: "1s"
+      steps:
+        - to:
+            uri: "telegram:bots"
+            parameters:
+              authorizationToken: "XXX"
 ----
 
 === Map values for parameters
@@ -326,16 +330,17 @@ configure _tags_ as a Map:
 
 [source,yaml]
 ----
-- from:
-    uri: "plc4x"
-    parameters:
-      driver: "some driver url here"
-      tags:
-        "tags_2": "XXX"
-        "tags_6": "YYY"
-    steps:
-      - to:
-          uri: "log:plc"
+- route:
+    from:
+      uri: "plc4x"
+      parameters:
+        driver: "some driver url here"
+        tags:
+          "tags_2": "XXX"
+          "tags_6": "YYY"
+      steps:
+        - to:
+            uri: "log:plc"
 ----
 
 In this example the _tags_ options is of Map type and can be configured using 
YAML map syntax.
@@ -671,27 +676,28 @@ as shown below:
 
 [source,yaml]
 ----
-- from:
-    uri: "direct:start"
-    steps:
-      - choice:
-          when:
-          - jsonpath:
-              expression: "person.middlename"
-              suppressExceptions: true
-            steps:
-            - to:
-                uri: mock:middle
-          - jsonpath:
-              expression: "person.lastname"
-              suppressExceptions: true
-            steps:
-            - to:
-                uri: mock:last
-          otherwise:
-            steps:
+- route:
+    from:
+      uri: "direct:start"
+      steps:
+        - choice:
+            when:
+            - jsonpath:
+                expression: "person.middlename"
+                suppressExceptions: true
+              steps:
+              - to:
+                  uri: mock:middle
+            - jsonpath:
+                expression: "person.lastname"
+                suppressExceptions: true
+              steps:
               - to:
-                  uri: mock:other
+                  uri: mock:last
+            otherwise:
+              steps:
+                - to:
+                    uri: mock:other
 ----
 
 In the route above, the following message
@@ -735,6 +741,7 @@ The canonical schema removes all implicit patterns:
 * No string shorthands: EIPs like `log` must use the object form with explicit 
properties.
 * No implicit expressions: Expression-aware EIPs require the `expression` 
wrapper.
 * No `oneOf`/`anyOf`/`not` constructs: The schema uses only simple `type: 
object` with `properties`.
+* A route is written under `route:`: a top-level `from:` is the compact 
notation of a route.
 
 This results in a schema that is approximately 25% smaller and significantly 
easier for tooling to process.
 
@@ -932,8 +939,9 @@ camel validate normalize --output normalized/ myroute.yaml
 Camel logs a WARN message when YAML routes use compact (shorthand) notation 
instead of
 the canonical (explicit) form: a step written as a string (`log: "..."`), a 
language written as a
 string (`simple: "..."`), or a language key directly on the EIP (`setBody: 
{simple: ...}`) instead of
-under `expression:`. This is to encourage adopting the canonical style which is
-more friendly for tooling and AI assistants.
+under `expression:`. A top-level `- from:` without `- route:` is compact 
notation as well: a route is
+written under `route:`, as an XML route is always a `<route>`. This is to 
encourage adopting the canonical
+style which is more friendly for tooling and AI assistants.
 
 The warning is logged once per resource file and looks like:
 
@@ -964,6 +972,7 @@ This reports each use of the compact notation with the 
canonical form to write,
 ----
 /0/route/from/steps/0/setBody: setBody: {simple: ...} is the deprecated 
compact notation: an expression is written under expression: (setBody: 
{expression: {simple: {expression: "..."}}}); camel validate normalize rewrites 
a file in the canonical format
 /0/route/from/steps/1/log: log: "..." is the deprecated compact notation: 
write log: {message: "..."}; camel validate normalize rewrites a file in the 
canonical format
+/1: a top-level from: is the deprecated compact notation: a route is written 
under route: (- route: {from: {uri: "...", steps: [...]}}); camel validate 
normalize rewrites a file in the canonical format
 ----
 
 The `YamlValidator` class supports both schemas programmatically:
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
index baae198c55d3..984dfb7c82ee 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/schemas/camelYamlDsl-model.json
@@ -22,13 +22,6 @@
         "title" : "Error Handler",
         "description" : "Configures error handling strategy for routes, 
controlling how failed exchanges are handled and redelivered",
         "label" : "configuration,error,errorhandling"
-      }, {
-        "name" : "from",
-        "type" : "object",
-        "ref" : "from",
-        "title" : "From",
-        "description" : "Defines the consumer endpoint that acts as the input 
source for a route",
-        "label" : "eip,endpoint,routing"
       }, {
         "name" : "intercept",
         "type" : "object",
@@ -865,58 +858,6 @@
         "type" : "string"
       } ]
     },
-    "from" : {
-      "title" : "From",
-      "description" : "Defines the consumer endpoint that acts as the input 
source for a route",
-      "label" : "eip,endpoint,routing",
-      "aliases" : [ "source", "ingress" ],
-      "isListItem" : true,
-      "children" : [ {
-        "name" : "id",
-        "type" : "string",
-        "displayName" : "Id",
-        "kind" : "attribute",
-        "index" : 0,
-        "group" : "common"
-      }, {
-        "name" : "note",
-        "type" : "string",
-        "displayName" : "Note",
-        "kind" : "attribute",
-        "index" : 1,
-        "group" : "common"
-      }, {
-        "name" : "description",
-        "type" : "string",
-        "displayName" : "Description",
-        "kind" : "attribute",
-        "index" : 2,
-        "group" : "common"
-      }, {
-        "name" : "uri",
-        "type" : "string",
-        "required" : true,
-        "displayName" : "Uri",
-        "kind" : "attribute",
-        "index" : 3,
-        "group" : "common"
-      }, {
-        "name" : "variableReceive",
-        "type" : "string",
-        "displayName" : "Variable Receive",
-        "kind" : "attribute",
-        "index" : 4,
-        "group" : "common"
-      }, {
-        "name" : "parameters",
-        "type" : "object"
-      }, {
-        "name" : "steps",
-        "type" : "array",
-        "ref" : "steps",
-        "required" : true
-      } ]
-    },
     "a2aSubTask" : {
       "title" : "A2A Sub Task",
       "description" : "Groups route steps and emits A2A (Agent-to-Agent) 
protocol progress events before, after, or when the grouped work fails",
@@ -3047,6 +2988,57 @@
         "ref" : "steps"
       } ]
     },
+    "from" : {
+      "title" : "From",
+      "description" : "Defines the consumer endpoint that acts as the input 
source for a route",
+      "label" : "eip,endpoint,routing",
+      "aliases" : [ "source", "ingress" ],
+      "children" : [ {
+        "name" : "id",
+        "type" : "string",
+        "displayName" : "Id",
+        "kind" : "attribute",
+        "index" : 0,
+        "group" : "common"
+      }, {
+        "name" : "note",
+        "type" : "string",
+        "displayName" : "Note",
+        "kind" : "attribute",
+        "index" : 1,
+        "group" : "common"
+      }, {
+        "name" : "description",
+        "type" : "string",
+        "displayName" : "Description",
+        "kind" : "attribute",
+        "index" : 2,
+        "group" : "common"
+      }, {
+        "name" : "uri",
+        "type" : "string",
+        "required" : true,
+        "displayName" : "Uri",
+        "kind" : "attribute",
+        "index" : 3,
+        "group" : "common"
+      }, {
+        "name" : "variableReceive",
+        "type" : "string",
+        "displayName" : "Variable Receive",
+        "kind" : "attribute",
+        "index" : 4,
+        "group" : "common"
+      }, {
+        "name" : "parameters",
+        "type" : "object"
+      }, {
+        "name" : "steps",
+        "type" : "array",
+        "ref" : "steps",
+        "required" : true
+      } ]
+    },
     "globalOption" : {
       "title" : "Global Option",
       "description" : "Defines a single global configuration option as a 
key/value pair for the CamelContext",
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
index 5938aeccfa92..a5b9bd21be8f 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_23.adoc
@@ -870,6 +870,16 @@ to the whole process, so a browser session served by 
`--web` would be recorded i
 file as the local session. Previously the combination was accepted, but 
recording never produced any
 output, so run the two modes in separate processes instead.
 
+=== camel-yaml-dsl - the canonical schema requires route:
+
+A top-level `- from:` without `- route:` is the compact notation of a route, 
and the canonical schema
+(`camelYamlDsl-canonical.json`) no longer lists `from` among its top-level 
entries: a route is written under
+`route:`, as an XML route is always a `<route>`, and as Kaoto and Karavan 
write it. `camel validate yaml --canonical`
+now reports a top-level `from:` with the form to write, the way it reports the 
other compact shapes, and
+`camel validate normalize` rewrites it. The Integration template of `camel 
init` writes its flows in the `- route:`
+form as well; a Kamelet's `template:` keeps its `from:`, which is the Kamelet 
spec's shape. The classic schema and `camel run` are unchanged: the file keeps 
working,
+with the compact notation warning `camel run` already logged for it.
+
 === camel-mail
 
 `MimeMultipartDataFormat` now uses `MailHeaderFilterStrategy` instead of a 
plain
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/integration.yaml.ftl
 
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/integration.yaml.ftl
index 3c200771ad7e..a043286bd553 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/integration.yaml.ftl
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/resources/templates/integration.yaml.ftl
@@ -22,14 +22,15 @@ metadata:
   name: [=Name]
 spec:
   flows:
-    - from:
-        uri: "timer:yaml"
-        parameters:
-          period: "1000"
-        steps:
-          - setBody:
-              expression:
-                constant:
-                  expression: "Hello Camel from yaml"
-          - log:
-              message: "${body}"
+    - route:
+        from:
+          uri: "timer:yaml"
+          parameters:
+            period: "1000"
+          steps:
+            - setBody:
+                expression:
+                  constant:
+                    expression: "Hello Camel from yaml"
+            - log:
+                message: "${body}"
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/TemplateHelperTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/TemplateHelperTest.java
index a7f91b4ef0d9..f583461a359c 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/TemplateHelperTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/common/TemplateHelperTest.java
@@ -524,10 +524,11 @@ class TemplateHelperTest {
     void testInitKameletTemplatesAreCanonical(String name) throws Exception {
         String content = loadInitTemplate(name).replace("[=Name]", 
"my-kamelet");
 
-        // the Kamelet CR wraps the route in spec.template as its from
+        // the Kamelet CR wraps the route in spec.template as its from (the 
Kamelet spec's shape); validated as the
+        // route it is, since a top-level from: is the compact notation of a 
route file (CAMEL-24745)
         Map<String, Object> cr = new Yaml().load(content);
         Map<String, Object> spec = (Map<String, Object>) cr.get("spec");
-        assertCanonical(name, new Yaml().dump(List.of(spec.get("template"))));
+        assertCanonical(name, new Yaml().dump(List.of(Map.of("route", 
spec.get("template")))));
     }
 
     private static void assertCanonical(String name, String yaml) throws 
Exception {
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
index 792d1b506f62..117822f3c546 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-maven-plugin/src/main/java/org/apache/camel/maven/dsl/yaml/GenerateYamlSchemaMojo.java
@@ -76,6 +76,9 @@ public class GenerateYamlSchemaMojo extends 
GenerateYamlSupportMojo {
     @Parameter(defaultValue = "false")
     private boolean canonical;
 
+    /** The top-level entries the canonical schema leaves out: the compact 
notation of a route. */
+    private static final Set<String> COMPACT_TOP_LEVEL_NODES = Set.of("from");
+
     private ObjectNode items;
     private ObjectNode definitions;
     private ObjectNode step;
@@ -135,6 +138,11 @@ public class GenerateYamlSchemaMojo extends 
GenerateYamlSupportMojo {
                 continue;
             }
             if (hasAnnotation(entry.getValue(), YAML_IN_ANNOTATION)) {
+                if (canonical && !nodes.isEmpty() && 
COMPACT_TOP_LEVEL_NODES.containsAll(nodes)) {
+                    // a top-level from: without route: is the compact 
notation of a route (the runtime warns on it,
+                    // CAMEL-24745): the canonical schema requires - route: 
{from: ...}, as XML requires <route>
+                    continue;
+                }
                 nodes.forEach(node -> {
                     items.withObject("/properties")
                             .putObject(node)
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/SchemaHints.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/SchemaHints.java
index e3f6102c0171..c5db70953cd3 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/SchemaHints.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/SchemaHints.java
@@ -372,6 +372,12 @@ final class SchemaHints {
      * line, and the normalize command.
      */
     static final List<Hint> COMPACT = List.of(
+            // - from: at the top level: the route is written under route:, as 
XML writes <route> (CAMEL-24745)
+            replace("additionalProperties", null,
+                    m -> "from".equals(m.unknown()) && m.nameIsIndex() && 
m.parentName().isEmpty(),
+                    m -> "a top-level from: is the deprecated compact 
notation: a route is written under route:"
+                         + " (- route: {from: {uri: \"...\", steps: [...]}})" 
+ NORMALIZE_HINT,
+                    COMPACT_NOTATION, COMPACT_NOTATION),
             // setBody: {simple: ...} or when: [- simple: ...]: the language 
key sits on the EIP, not under expression:
             replace("additionalProperties", null,
                     m -> m.unknown() != null && 
m.validator().languageKeys().contains(m.unknown()),
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
index 73dd235aea44..43cc7bbdb376 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlCanonicalValidatorTest.java
@@ -164,6 +164,24 @@ public class YamlCanonicalValidatorTest {
     }
 
     // CAMEL-24723: the compact notation is reported as such, one message per 
occurrence, with the canonical form
+    @Test
+    public void testTopLevelFromIsReportedWithTheRouteForm() throws Exception {
+        // CAMEL-24745: a top-level from: is the compact notation of a route; 
the classic schema accepts it, the
+        // canonical schema requires route:, as XML requires <route>
+        var route = """
+                - from:
+                    uri: "direct:start"
+                    steps:
+                      - to:
+                          uri: "mock:a"
+                """;
+        assertThat(classicValidator.validate(route)).isEmpty();
+        assertThat(canonicalValidator.validate(route)).extracting(e -> 
e.getInstanceLocation() + ": " + e.getMessage())
+                .containsExactly("/0: a top-level from: is the deprecated 
compact notation: a route is written under"
+                                 + " route: (- route: {from: {uri: \"...\", 
steps: [...]}});"
+                                 + " camel validate normalize rewrites a file 
in the canonical format");
+    }
+
     @Test
     public void testCompactNotationIsReportedWithTheCanonicalForm() throws 
Exception {
         var route = """
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorExpressionHintTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorExpressionHintTest.java
index f6668272a421..0f04e2ba86cb 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorExpressionHintTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorExpressionHintTest.java
@@ -73,18 +73,19 @@ public class YamlValidatorExpressionHintTest {
     @Test
     public void testAggregateCompletionSizeAsPlainValue() {
         String yaml = """
-                - from:
-                    uri: timer:tick
-                    steps:
-                      - aggregate:
-                          correlationExpression:
-                            constant:
-                              expression: "a"
-                          completionSizeExpression: 10
-                          aggregationStrategy: myStrategy
-                          steps:
-                            - log:
-                                message: "${body}"
+                - route:
+                    from:
+                      uri: timer:tick
+                      steps:
+                        - aggregate:
+                            correlationExpression:
+                              constant:
+                                expression: "a"
+                            completionSizeExpression: 10
+                            aggregationStrategy: myStrategy
+                            steps:
+                              - log:
+                                  message: "${body}"
                 """;
         assertHint(yaml, "completionSizeExpression: {constant: {expression: 
\"10\"}}");
     }
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorScalarLeniencyTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorScalarLeniencyTest.java
index 961f31a64cb9..29e41035a10d 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorScalarLeniencyTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorScalarLeniencyTest.java
@@ -108,24 +108,26 @@ public class YamlValidatorScalarLeniencyTest {
     public void testStepsAsMapStillRejected() {
         // a map where the schema expects a list is what the runtime rejects 
with "Node type map is invalid, expected array"
         assertRejectedYaml("""
-                - from:
-                    uri: timer:tick
-                    steps:
-                      log: "hi"
+                - route:
+                    from:
+                      uri: timer:tick
+                      steps:
+                        log: "hi"
                 """, "array expected");
     }
 
     @Test
     public void testWhenAsMapStillRejected() {
         assertRejectedYaml("""
-                - from:
-                    uri: timer:tick
-                    steps:
-                      - choice:
-                          when:
-                            simple: "${body} == 1"
-                            steps:
-                              - log: "one"
+                - route:
+                    from:
+                      uri: timer:tick
+                      steps:
+                        - choice:
+                            when:
+                              simple: "${body} == 1"
+                              steps:
+                                - log: "one"
                 """, "array expected");
     }
 
@@ -136,19 +138,21 @@ public class YamlValidatorScalarLeniencyTest {
                     get:
                       path: /hello
                       to: direct:hello
-                - from:
-                    uri: direct:hello
-                    steps:
-                      - log: "hi"
+                - route:
+                    from:
+                      uri: direct:hello
+                      steps:
+                        - log: "hi"
                 """, "array expected");
     }
 
     @Test
     public void testScalarWhereStepsExpectedStillRejected() {
         assertRejectedYaml("""
-                - from:
-                    uri: timer:tick
-                    steps: "log:hi"
+                - route:
+                    from:
+                      uri: timer:tick
+                      steps: "log:hi"
                 """, "array expected");
     }
 
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
index b8a2fb4bb707..9d8fc986b152 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-canonical.json
@@ -284,37 +284,6 @@
         },
         "required" : [ "steps", "uri" ]
       },
-      
"org.apache.camel.dsl.yaml.deserializers.RouteFromDefinitionDeserializer" : {
-        "type" : "object",
-        "additionalProperties" : false,
-        "properties" : {
-          "description" : {
-            "type" : "string"
-          },
-          "id" : {
-            "type" : "string"
-          },
-          "note" : {
-            "type" : "string"
-          },
-          "parameters" : {
-            "type" : "object"
-          },
-          "steps" : {
-            "type" : "array",
-            "items" : {
-              "$ref" : 
"#/items/definitions/org.apache.camel.model.ProcessorDefinition"
-            }
-          },
-          "uri" : {
-            "type" : "string"
-          },
-          "variableReceive" : {
-            "type" : "string"
-          }
-        },
-        "required" : [ "steps", "uri" ]
-      },
       "org.apache.camel.model.A2ASubTaskDefinition" : {
         "title" : "A2A Sub Task",
         "description" : "Groups route steps and emits A2A (Agent-to-Agent) 
protocol progress events before, after, or when the grouped work fails",
@@ -13338,9 +13307,6 @@
       "errorHandler" : {
         "$ref" : 
"#/items/definitions/org.apache.camel.dsl.yaml.deserializers.ErrorHandlerDeserializer"
       },
-      "from" : {
-        "$ref" : 
"#/items/definitions/org.apache.camel.dsl.yaml.deserializers.RouteFromDefinitionDeserializer"
-      },
       "intercept" : {
         "$ref" : 
"#/items/definitions/org.apache.camel.model.InterceptDefinition"
       },
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
index baae198c55d3..984dfb7c82ee 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/generated/resources/schema/camelYamlDsl-model.json
@@ -22,13 +22,6 @@
         "title" : "Error Handler",
         "description" : "Configures error handling strategy for routes, 
controlling how failed exchanges are handled and redelivered",
         "label" : "configuration,error,errorhandling"
-      }, {
-        "name" : "from",
-        "type" : "object",
-        "ref" : "from",
-        "title" : "From",
-        "description" : "Defines the consumer endpoint that acts as the input 
source for a route",
-        "label" : "eip,endpoint,routing"
       }, {
         "name" : "intercept",
         "type" : "object",
@@ -865,58 +858,6 @@
         "type" : "string"
       } ]
     },
-    "from" : {
-      "title" : "From",
-      "description" : "Defines the consumer endpoint that acts as the input 
source for a route",
-      "label" : "eip,endpoint,routing",
-      "aliases" : [ "source", "ingress" ],
-      "isListItem" : true,
-      "children" : [ {
-        "name" : "id",
-        "type" : "string",
-        "displayName" : "Id",
-        "kind" : "attribute",
-        "index" : 0,
-        "group" : "common"
-      }, {
-        "name" : "note",
-        "type" : "string",
-        "displayName" : "Note",
-        "kind" : "attribute",
-        "index" : 1,
-        "group" : "common"
-      }, {
-        "name" : "description",
-        "type" : "string",
-        "displayName" : "Description",
-        "kind" : "attribute",
-        "index" : 2,
-        "group" : "common"
-      }, {
-        "name" : "uri",
-        "type" : "string",
-        "required" : true,
-        "displayName" : "Uri",
-        "kind" : "attribute",
-        "index" : 3,
-        "group" : "common"
-      }, {
-        "name" : "variableReceive",
-        "type" : "string",
-        "displayName" : "Variable Receive",
-        "kind" : "attribute",
-        "index" : 4,
-        "group" : "common"
-      }, {
-        "name" : "parameters",
-        "type" : "object"
-      }, {
-        "name" : "steps",
-        "type" : "array",
-        "ref" : "steps",
-        "required" : true
-      } ]
-    },
     "a2aSubTask" : {
       "title" : "A2A Sub Task",
       "description" : "Groups route steps and emits A2A (Agent-to-Agent) 
protocol progress events before, after, or when the grouped work fails",
@@ -3047,6 +2988,57 @@
         "ref" : "steps"
       } ]
     },
+    "from" : {
+      "title" : "From",
+      "description" : "Defines the consumer endpoint that acts as the input 
source for a route",
+      "label" : "eip,endpoint,routing",
+      "aliases" : [ "source", "ingress" ],
+      "children" : [ {
+        "name" : "id",
+        "type" : "string",
+        "displayName" : "Id",
+        "kind" : "attribute",
+        "index" : 0,
+        "group" : "common"
+      }, {
+        "name" : "note",
+        "type" : "string",
+        "displayName" : "Note",
+        "kind" : "attribute",
+        "index" : 1,
+        "group" : "common"
+      }, {
+        "name" : "description",
+        "type" : "string",
+        "displayName" : "Description",
+        "kind" : "attribute",
+        "index" : 2,
+        "group" : "common"
+      }, {
+        "name" : "uri",
+        "type" : "string",
+        "required" : true,
+        "displayName" : "Uri",
+        "kind" : "attribute",
+        "index" : 3,
+        "group" : "common"
+      }, {
+        "name" : "variableReceive",
+        "type" : "string",
+        "displayName" : "Variable Receive",
+        "kind" : "attribute",
+        "index" : 4,
+        "group" : "common"
+      }, {
+        "name" : "parameters",
+        "type" : "object"
+      }, {
+        "name" : "steps",
+        "type" : "array",
+        "ref" : "steps",
+        "required" : true
+      } ]
+    },
     "globalOption" : {
       "title" : "Global Option",
       "description" : "Defines a single global configuration option as a 
key/value pair for the CamelContext",
diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
index c1ad1cd0126c..0f9c094dcc45 100644
--- a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
+++ b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/main/docs/yaml-dsl.adoc
@@ -19,20 +19,21 @@ A route is collection of elements defined as follows:
 
 [source,yaml]
 ----
-- from: #<1>
-    uri: "direct:start"
-    steps: #<2>
-      - filter:
-          expression:
-            simple: "${in.header.continue} == true"
-          steps: #<2>
-            - to:
-                uri: "log:filtered"
-      - to:
-          uri: "log:original"
+- route: #<1>
+    from:
+      uri: "direct:start"
+      steps: #<2>
+        - filter:
+            expression:
+              simple: "${in.header.continue} == true"
+            steps: #<2>
+              - to:
+                  uri: "log:filtered"
+        - to:
+            uri: "log:original"
 ----
 
-<1> route entry point, by default `from` and `rest` are supported
+<1> a route, with the `from` endpoint it consumes from (`rest`, `beans`, 
`onException` and more are the other top-level entries)
 <2> processing steps
 
 [NOTE]
@@ -190,14 +191,15 @@ This allows YAML such as:
 
 [source,yaml]
 ----
-- from:
-    uri: "direct:start"
-    steps:
-      - myStep:
-          id: "custom-step"
-          steps:
-            - to:
-                uri: "mock:result"
+- route:
+    from:
+      uri: "direct:start"
+      steps:
+        - myStep:
+            id: "custom-step"
+            steps:
+              - to:
+                  uri: "mock:result"
 ----
 
 Resolvers are discovered when YAML routes are parsed. Camel starts with the 
built-in YAML DSL resolvers, then adds the
@@ -295,25 +297,27 @@ To define an endpoint with the YAML dsl you have two 
options:
 +
 [source,yaml]
 ----
-- from:
-    uri: "timer:tick?period=1s"
-    steps:
-      - to:
-          uri: "telegram:bots?authorizationToken=XXX"
+- route:
+    from:
+      uri: "timer:tick?period=1s"
+      steps:
+        - to:
+            uri: "telegram:bots?authorizationToken=XXX"
 ----
 . Using URI and parameters:
 +
 [source,yaml]
 ----
-- from:
-    uri: "timer://tick"
-    parameters:
-      period: "1s"
-    steps:
-      - to:
-          uri: "telegram:bots"
-          parameters:
-            authorizationToken: "XXX"
+- route:
+    from:
+      uri: "timer://tick"
+      parameters:
+        period: "1s"
+      steps:
+        - to:
+            uri: "telegram:bots"
+            parameters:
+              authorizationToken: "XXX"
 ----
 
 === Map values for parameters
@@ -326,16 +330,17 @@ configure _tags_ as a Map:
 
 [source,yaml]
 ----
-- from:
-    uri: "plc4x"
-    parameters:
-      driver: "some driver url here"
-      tags:
-        "tags_2": "XXX"
-        "tags_6": "YYY"
-    steps:
-      - to:
-          uri: "log:plc"
+- route:
+    from:
+      uri: "plc4x"
+      parameters:
+        driver: "some driver url here"
+        tags:
+          "tags_2": "XXX"
+          "tags_6": "YYY"
+      steps:
+        - to:
+            uri: "log:plc"
 ----
 
 In this example the _tags_ options is of Map type and can be configured using 
YAML map syntax.
@@ -671,27 +676,28 @@ as shown below:
 
 [source,yaml]
 ----
-- from:
-    uri: "direct:start"
-    steps:
-      - choice:
-          when:
-          - jsonpath:
-              expression: "person.middlename"
-              suppressExceptions: true
-            steps:
-            - to:
-                uri: mock:middle
-          - jsonpath:
-              expression: "person.lastname"
-              suppressExceptions: true
-            steps:
-            - to:
-                uri: mock:last
-          otherwise:
-            steps:
+- route:
+    from:
+      uri: "direct:start"
+      steps:
+        - choice:
+            when:
+            - jsonpath:
+                expression: "person.middlename"
+                suppressExceptions: true
+              steps:
+              - to:
+                  uri: mock:middle
+            - jsonpath:
+                expression: "person.lastname"
+                suppressExceptions: true
+              steps:
               - to:
-                  uri: mock:other
+                  uri: mock:last
+            otherwise:
+              steps:
+                - to:
+                    uri: mock:other
 ----
 
 In the route above, the following message
@@ -735,6 +741,7 @@ The canonical schema removes all implicit patterns:
 * No string shorthands: EIPs like `log` must use the object form with explicit 
properties.
 * No implicit expressions: Expression-aware EIPs require the `expression` 
wrapper.
 * No `oneOf`/`anyOf`/`not` constructs: The schema uses only simple `type: 
object` with `properties`.
+* A route is written under `route:`: a top-level `from:` is the compact 
notation of a route.
 
 This results in a schema that is approximately 25% smaller and significantly 
easier for tooling to process.
 
@@ -932,8 +939,9 @@ camel validate normalize --output normalized/ myroute.yaml
 Camel logs a WARN message when YAML routes use compact (shorthand) notation 
instead of
 the canonical (explicit) form: a step written as a string (`log: "..."`), a 
language written as a
 string (`simple: "..."`), or a language key directly on the EIP (`setBody: 
{simple: ...}`) instead of
-under `expression:`. This is to encourage adopting the canonical style which is
-more friendly for tooling and AI assistants.
+under `expression:`. A top-level `- from:` without `- route:` is compact 
notation as well: a route is
+written under `route:`, as an XML route is always a `<route>`. This is to 
encourage adopting the canonical
+style which is more friendly for tooling and AI assistants.
 
 The warning is logged once per resource file and looks like:
 
@@ -964,6 +972,7 @@ This reports each use of the compact notation with the 
canonical form to write,
 ----
 /0/route/from/steps/0/setBody: setBody: {simple: ...} is the deprecated 
compact notation: an expression is written under expression: (setBody: 
{expression: {simple: {expression: "..."}}}); camel validate normalize rewrites 
a file in the canonical format
 /0/route/from/steps/1/log: log: "..." is the deprecated compact notation: 
write log: {message: "..."}; camel validate normalize rewrites a file in the 
canonical format
+/1: a top-level from: is the deprecated compact notation: a route is written 
under route: (- route: {from: {uri: "...", steps: [...]}}); camel validate 
normalize rewrites a file in the canonical format
 ----
 
 The `YamlValidator` class supports both schemas programmatically:
diff --git 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/YamlCompletionTreeTest.java
 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/YamlCompletionTreeTest.java
index b025c9efc12a..73872c14a910 100644
--- 
a/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/YamlCompletionTreeTest.java
+++ 
b/dsl/camel-yaml-dsl/camel-yaml-dsl/src/test/java/org/apache/camel/dsl/yaml/YamlCompletionTreeTest.java
@@ -55,9 +55,11 @@ class YamlCompletionTreeTest {
         assertThat(root).as("root node must exist").isNotNull();
 
         Set<String> names = childNames(root);
-        assertThat(names).contains("route", "from", "rest", "beans",
+        assertThat(names).contains("route", "rest", "beans",
                 "errorHandler", "onException", "routeConfiguration",
                 "intercept", "interceptFrom", "interceptSendToEndpoint");
+        // a top-level from: is the compact notation of a route: the canonical 
tree has it under route only (CAMEL-24745)
+        assertThat(names).doesNotContain("from");
     }
 
     @Test

Reply via email to