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 5b9cf8ffd139 CAMEL-24857, CAMEL-24858, CAMEL-24859: two validator rows 
and the reload outcome in camel_write_file, from the stepwise benchmark
5b9cf8ffd139 is described below

commit 5b9cf8ffd1392ac7e8f5abca0b93e5f419337b91
Author: Claus Ibsen <[email protected]>
AuthorDate: Mon Sep 21 11:12:27 2026 +0200

    CAMEL-24857, CAMEL-24858, CAMEL-24859: two validator rows and the reload 
outcome in camel_write_file, from the stepwise benchmark
    
    ## Description
    
    Three findings of the stepwise half of the round-2 local-model benchmark 
(ten camel-jbang-examples built step by step through the camel-jbang-mcp 
server, five runs each). Together they were the cause of most systematic 
failures of the series: the model wrote something the validator accepted, the 
runtime rejected it on the reload, and the write tool had already said 
"written".
    
    **CAMEL-24857, a `${...}` placeholder where `{{...}}` is meant.** `period: 
"${welcome.period}"`, `period: ${properties:welcome.period}` on an endpoint, 
`start: ${order.first-number}` on a bean property: Simple syntax for a property 
key. The endpoint checks and a new bean property check report it with the form 
to write:
    
    ```
    Line 6: timer: period=${welcome.period} is a Simple expression, which an 
endpoint option is not evaluated as: a property placeholder is written {{key}}, 
so period: "{{welcome.period}}"
    ```
    
    Only a property key in Simple syntax is reported (`${welcome.period}`, 
`${properties:x}`): a value whose first segment is a Simple root (`${body.id}`, 
`${header.x}`, `${date:...}`, `${exception.message}`) is a Simple expression 
and is left alone, as is the uri of `toD`, `wireTap`, `enrich`, `pollEnrich`, 
`recipientList`, `routingSlip` and `dynamicRouter`, which evaluate it as 
Simple. Seven of eight failures of two steps in the series were this shape.
    
    **CAMEL-24858, a required path option missing.** `uri: cron` with only a 
schedule under parameters passed `camel validate`; `camel run` fails with 
"Option name is required when creating endpoint uri with syntax cron:name", and 
in dev mode the reload fails with a WARN. A `from:` or `to:` with a bare 
scheme, no path and no `//`, whose component has a required path option that is 
not among the parameters either, is reported with both places it can go. An 
explicit empty authority (`infini [...]
    
    **CAMEL-24859, the reload outcome in the write tool.** When an integration 
of the project is selected and runs in dev mode, `camel_write_file` remembers 
the newest reload record before the write, waits up to eight seconds for a 
newer one, and answers with it: `reload.status` reloaded, `properties`, 
`failed` with the cause (the first lines, no stack frames) and the validator's 
report the runtime logs next to a load failure (CAMEL-24851), or `unknown` when 
nothing came. The message says [...]
    
    Two documentation examples had the placeholder slip (mongodb, 
mongodb-gridfs: `database=${mongodb.database}`) and one had a nameless direct 
endpoint in all three forms (spring-cloud-config, `from("direct")`): fixed, 
with their catalog copies. Upgrade guide note for the tool.
    
    ## Tests
    
    - `SourceValidatorPlaceholderTest`: the endpoint option and bean property 
cases with the form to write, the dynamic EIP and `{{...}}` left alone, the 
missing `name` on cron reported and not reported when given in the uri or under 
parameters.
    - `ReloadOutcomeTest` on synthetic log records: the baseline before the 
write, a failed reload with the cause and the validator's report and no stack 
frames, a later successful reload.
    - All 228 tests of the ai package of camel-jbang-core green, including the 
documentation examples test that runs every YAML example of the docs through 
the checks (which is how the three doc slips surfaced).
---
 .../camel/catalog/docs/mongodb-component.adoc      |   8 +-
 .../catalog/docs/mongodb-gridfs-component.adoc     |   6 +-
 .../camel/catalog/docs/spring-cloud-config.adoc    |  12 +-
 .../src/main/docs/mongodb-gridfs-component.adoc    |   6 +-
 .../src/main/docs/mongodb-component.adoc           |   8 +-
 .../src/main/docs/spring-cloud-config.adoc         |  12 +-
 .../ROOT/pages/camel-4x-upgrade-guide-4_23.adoc    |   5 +
 .../dsl/jbang/core/commands/ai/AuthoringTools.java |  30 ++++-
 .../dsl/jbang/core/commands/ai/BeanRefChecks.java  |  55 ++++++++
 .../dsl/jbang/core/commands/ai/EndpointChecks.java |  84 ++++++++++++
 .../dsl/jbang/core/commands/ai/ReloadOutcome.java  | 144 +++++++++++++++++++++
 .../dsl/jbang/core/commands/ai/YamlLines.java      |  36 ++++++
 .../jbang/core/commands/ai/ReloadOutcomeTest.java  |  97 ++++++++++++++
 .../ai/SourceValidatorPlaceholderTest.java         | 109 ++++++++++++++++
 14 files changed, 584 insertions(+), 28 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mongodb-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mongodb-component.adoc
index c4517c7b59e8..4b54aeb69976 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mongodb-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mongodb-component.adoc
@@ -1328,7 +1328,7 @@ Java::
 [source,java]
 ----
 from("direct:start")
-    
.to("mongodb:mongoBean?database=${mongodb.database}&collection=${mongodb.collection}&operation=getDbStats")
+    
.to("mongodb:mongoBean?database={{mongodb.database}}&collection={{mongodb.collection}}&operation=getDbStats")
     .to("direct:result");
 ----
 
@@ -1339,7 +1339,7 @@ XML::
 <route>
   <from uri="direct:start" />
   <!-- using bean 'mongoBean' defined above -->
-  <to 
uri="mongodb:mongoBean?database=${mongodb.database}&amp;collection=${mongodb.collection}&amp;operation=getDbStats"
 />
+  <to 
uri="mongodb:mongoBean?database={{mongodb.database}}&amp;collection={{mongodb.collection}}&amp;operation=getDbStats"
 />
   <to uri="direct:result" />
 </route>
 ----
@@ -1355,8 +1355,8 @@ YAML::
         - to:
             uri: mongodb:mongoBean
             parameters:
-              database: "${mongodb.database}"
-              collection: "${mongodb.collection}"
+              database: "{{mongodb.database}}"
+              collection: "{{mongodb.collection}}"
               operation: getDbStats
         - to:
             uri: direct:result
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mongodb-gridfs-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mongodb-gridfs-component.adoc
index 9fbbf6a7e143..1e4b984069e3 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mongodb-gridfs-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/mongodb-gridfs-component.adoc
@@ -139,7 +139,7 @@ Java::
 [source,java]
 ----
 from("direct:start")
-    
.to("mongodb-gridfs:mongoBean?database=${mongodb.database}&operation=findOne")
+    
.to("mongodb-gridfs:mongoBean?database={{mongodb.database}}&operation=findOne")
     .to("direct:result");
 ----
 
@@ -150,7 +150,7 @@ XML::
 <route>
   <from uri="direct:start" />
   <!-- using bean 'mongoBean' defined above -->
-  <to 
uri="mongodb-gridfs:mongoBean?database=${mongodb.database}&amp;operation=findOne"
 />
+  <to 
uri="mongodb-gridfs:mongoBean?database={{mongodb.database}}&amp;operation=findOne"
 />
   <to uri="direct:result" />
 </route>
 ----
@@ -166,7 +166,7 @@ YAML::
         - to:
             uri: mongodb-gridfs:mongoBean
             parameters:
-              database: "${mongodb.database}"
+              database: "{{mongodb.database}}"
               operation: findOne
         - to:
             uri: direct:result
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-cloud-config.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-cloud-config.adoc
index 0d6241f66323..4ac8352ac0c2 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-cloud-config.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-cloud-config.adoc
@@ -86,9 +86,9 @@ Java::
 +
 [source,java]
 ----
-from("direct")
+from("direct:start")
   .setHeader("MyHeader", simple("{{spring-config:custom.property}}"))
-  .to("log");
+  .to("log:info");
 ----
 
 XML::
@@ -96,11 +96,11 @@ XML::
 [source,xml]
 ----
 <route>
-  <from uri="direct"/>
+  <from uri="direct:start"/>
   <setHeader name="MyHeader">
     <simple>{{spring-config:custom.property}}</simple>
   </setHeader>
-  <to uri="log"/>
+  <to uri="log:info"/>
 </route>
 ----
 
@@ -110,13 +110,13 @@ YAML::
 ----
 - route:
     from:
-      uri: direct
+      uri: direct:start
       steps:
         - setHeader:
             name: MyHeader
             simple: "{{spring-config:custom.property}}"
         - to:
-            uri: log
+            uri: log:info
 ----
 ====
 
diff --git 
a/components/camel-mongodb-gridfs/src/main/docs/mongodb-gridfs-component.adoc 
b/components/camel-mongodb-gridfs/src/main/docs/mongodb-gridfs-component.adoc
index 9fbbf6a7e143..1e4b984069e3 100644
--- 
a/components/camel-mongodb-gridfs/src/main/docs/mongodb-gridfs-component.adoc
+++ 
b/components/camel-mongodb-gridfs/src/main/docs/mongodb-gridfs-component.adoc
@@ -139,7 +139,7 @@ Java::
 [source,java]
 ----
 from("direct:start")
-    
.to("mongodb-gridfs:mongoBean?database=${mongodb.database}&operation=findOne")
+    
.to("mongodb-gridfs:mongoBean?database={{mongodb.database}}&operation=findOne")
     .to("direct:result");
 ----
 
@@ -150,7 +150,7 @@ XML::
 <route>
   <from uri="direct:start" />
   <!-- using bean 'mongoBean' defined above -->
-  <to 
uri="mongodb-gridfs:mongoBean?database=${mongodb.database}&amp;operation=findOne"
 />
+  <to 
uri="mongodb-gridfs:mongoBean?database={{mongodb.database}}&amp;operation=findOne"
 />
   <to uri="direct:result" />
 </route>
 ----
@@ -166,7 +166,7 @@ YAML::
         - to:
             uri: mongodb-gridfs:mongoBean
             parameters:
-              database: "${mongodb.database}"
+              database: "{{mongodb.database}}"
               operation: findOne
         - to:
             uri: direct:result
diff --git a/components/camel-mongodb/src/main/docs/mongodb-component.adoc 
b/components/camel-mongodb/src/main/docs/mongodb-component.adoc
index c4517c7b59e8..4b54aeb69976 100644
--- a/components/camel-mongodb/src/main/docs/mongodb-component.adoc
+++ b/components/camel-mongodb/src/main/docs/mongodb-component.adoc
@@ -1328,7 +1328,7 @@ Java::
 [source,java]
 ----
 from("direct:start")
-    
.to("mongodb:mongoBean?database=${mongodb.database}&collection=${mongodb.collection}&operation=getDbStats")
+    
.to("mongodb:mongoBean?database={{mongodb.database}}&collection={{mongodb.collection}}&operation=getDbStats")
     .to("direct:result");
 ----
 
@@ -1339,7 +1339,7 @@ XML::
 <route>
   <from uri="direct:start" />
   <!-- using bean 'mongoBean' defined above -->
-  <to 
uri="mongodb:mongoBean?database=${mongodb.database}&amp;collection=${mongodb.collection}&amp;operation=getDbStats"
 />
+  <to 
uri="mongodb:mongoBean?database={{mongodb.database}}&amp;collection={{mongodb.collection}}&amp;operation=getDbStats"
 />
   <to uri="direct:result" />
 </route>
 ----
@@ -1355,8 +1355,8 @@ YAML::
         - to:
             uri: mongodb:mongoBean
             parameters:
-              database: "${mongodb.database}"
-              collection: "${mongodb.collection}"
+              database: "{{mongodb.database}}"
+              collection: "{{mongodb.collection}}"
               operation: getDbStats
         - to:
             uri: direct:result
diff --git 
a/components/camel-spring-parent/camel-spring-cloud-config/src/main/docs/spring-cloud-config.adoc
 
b/components/camel-spring-parent/camel-spring-cloud-config/src/main/docs/spring-cloud-config.adoc
index 0d6241f66323..4ac8352ac0c2 100644
--- 
a/components/camel-spring-parent/camel-spring-cloud-config/src/main/docs/spring-cloud-config.adoc
+++ 
b/components/camel-spring-parent/camel-spring-cloud-config/src/main/docs/spring-cloud-config.adoc
@@ -86,9 +86,9 @@ Java::
 +
 [source,java]
 ----
-from("direct")
+from("direct:start")
   .setHeader("MyHeader", simple("{{spring-config:custom.property}}"))
-  .to("log");
+  .to("log:info");
 ----
 
 XML::
@@ -96,11 +96,11 @@ XML::
 [source,xml]
 ----
 <route>
-  <from uri="direct"/>
+  <from uri="direct:start"/>
   <setHeader name="MyHeader">
     <simple>{{spring-config:custom.property}}</simple>
   </setHeader>
-  <to uri="log"/>
+  <to uri="log:info"/>
 </route>
 ----
 
@@ -110,13 +110,13 @@ YAML::
 ----
 - route:
     from:
-      uri: direct
+      uri: direct:start
       steps:
         - setHeader:
             name: MyHeader
             simple: "{{spring-config:custom.property}}"
         - to:
-            uri: log
+            uri: log:info
 ----
 ====
 
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 adf72d1195cf..c1176d685d12 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
@@ -836,6 +836,11 @@ same `camel_` names by both MCP servers, `camel mcp` and 
`camel tui --mcp`: `cam
 `camel_catalog_find`, `camel_validate_source`, `camel_get_files`, 
`camel_write_file`, `camel_run`,
 `camel_control`, `camel_get_log`, `camel_get_errors`, `camel_eval_expression` 
and `camel_error_diagnose`.
 
+The `camel_write_file` tool, when an integration of the project is selected 
and runs in dev mode, waits up to
+eight seconds for the reload of the written file and answers with its outcome 
(`reload.status` reloaded, failed
+with the cause and the validator's report, properties, or unknown), so an 
agent does not go on with a route that
+did not load. A write with no selected integration answers as before.
+
 In the TUI MCP server the tools `tui_catalog_doc`, `tui_validate_source`, 
`tui_write_file`, `tui_get_files`,
 `tui_control`, `tui_get_log`, `tui_get_errors` and `tui_eval_expression` were 
renamed to the `camel_` names
 above; the `tui_` prefix is now reserved for the tools that only make sense in 
front of the screen. Update
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/AuthoringTools.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/AuthoringTools.java
index 56f5c5b87a49..684cc67872a2 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/AuthoringTools.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/AuthoringTools.java
@@ -359,6 +359,9 @@ public final class AuthoringTools {
         return result;
     }
 
+    /** How long a write waits for the running integration's reload record 
before answering without it. */
+    static final long RELOAD_WAIT_MILLIS = 8000;
+
     /** Writes a file after validating it, as {@code camel_write_file} does; 
no confirmation is asked here. */
     public static JsonObject writeFile(ToolContext ctx, Path dir, String file, 
String content, boolean validate) {
         Path path = resolveFile(dir, file);
@@ -378,6 +381,16 @@ public final class AuthoringTools {
                 return result;
             }
         }
+        // the reload of a running integration is reported in the answer 
(CAMEL-24859): the reload records newer
+        // than the ones before the write
+        String processName = null;
+        String sinceKey = null;
+        boolean watch = ctx.hasProcess() && 
SourceValidator.isValidatableFile(file);
+        if (watch) {
+            RuntimeHelper.ProcessInfo p = 
RuntimeHelper.findProcess(Long.toString(ctx.pid()));
+            processName = p != null ? p.name() : null;
+            sinceKey = 
ReloadOutcome.latestReloadKey(ReloadOutcome.records(ctx.pid(), processName));
+        }
         try {
             Files.createDirectories(path.getParent());
             Files.writeString(path, content, StandardCharsets.UTF_8);
@@ -390,8 +403,21 @@ public final class AuthoringTools {
         result.put("directory", dir.toString());
         result.put("lines", content.isEmpty() ? 0 : (int) 
content.lines().count());
         result.put("bytes", content.getBytes(StandardCharsets.UTF_8).length);
-        result.put("message", "An integration running the file in dev mode 
reloads it now; otherwise restart the"
-                              + " integration for the change to take effect.");
+        if (watch) {
+            JsonObject reload = ReloadOutcome.await(ctx.pid(), processName, 
sinceKey, RELOAD_WAIT_MILLIS);
+            result.put("reload", reload);
+            String status = reload.getString("status");
+            result.put("message", switch (status) {
+                case "reloaded" -> "The running integration reloaded the 
file.";
+                case "properties" -> "The running integration reloaded the 
properties.";
+                case "failed" -> "The running integration FAILED to reload the 
file, the route is not running; fix the"
+                                 + " content and write again (see 
reload.message).";
+                default -> "Written; " + reload.getString("message");
+            });
+        } else {
+            result.put("message", "An integration running the file in dev mode 
reloads it now; otherwise restart the"
+                                  + " integration for the change to take 
effect.");
+        }
         return result;
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/BeanRefChecks.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/BeanRefChecks.java
index 5ea3ef07ece2..a6df58c31317 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/BeanRefChecks.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/BeanRefChecks.java
@@ -158,6 +158,60 @@ final class BeanRefChecks {
         return names;
     }
 
+    /** A bean property line: key: value, under properties: of a bean. */
+    private static final Pattern PROPERTY_LINE = 
Pattern.compile("^\\s*([A-Za-z_][\\w.-]*):\\s*(.+?)\\s*$");
+
+    /**
+     * properties: {start: ${order.first-number}} on a bean: a Simple 
expression, which a bean property is not; the
+     * placeholder is {{order.first-number}}. The runtime fails to bind the 
property ("Error binding property
+     * (start=${order.first-number})"), and camel validate said nothing 
(CAMEL-24857).
+     */
+    static List<String> validateBeanPropertyPlaceholders(String content) {
+        List<String> msgs = new ArrayList<>();
+        String[] lines = content.split("\n", -1);
+        int blockIndent = -1;
+        int propsIndent = -1;
+        for (int i = 0; i < lines.length; i++) {
+            String line = lines[i];
+            if (line.isBlank() || line.trim().startsWith("#")) {
+                continue;
+            }
+            String trimmed = line.trim();
+            int indent = countLeadingSpaces(line);
+            if (blockIndent >= 0 && indent <= blockIndent) {
+                blockIndent = -1;
+                propsIndent = -1;
+            }
+            if (blockIndent < 0) {
+                if (trimmed.equals("- beans:") || trimmed.equals("beans:")) {
+                    blockIndent = indent;
+                }
+                continue;
+            }
+            if (propsIndent >= 0 && indent <= propsIndent) {
+                propsIndent = -1;
+            }
+            if (trimmed.equals("properties:")) {
+                propsIndent = indent;
+                continue;
+            }
+            if (propsIndent < 0) {
+                continue;
+            }
+            Matcher m = PROPERTY_LINE.matcher(line);
+            if (m.find()) {
+                String value = unquote(m.group(2));
+                if (YamlLines.isPropertyKeyInSimpleSyntax(value)) {
+                    String key = YamlLines.propertyKeyOf(value);
+                    msgs.add("Line " + (i + 1) + ": " + m.group(1) + ": " + 
value + " is a Simple expression, which a bean"
+                             + " property is not evaluated as: a property 
placeholder is written {{key}}, so "
+                             + m.group(1) + ": \"{{" + key + "}}\"");
+                }
+            }
+        }
+        return msgs;
+    }
+
     /**
      * Bean references in the YAML that nothing declares, each with how to 
declare it. A reference that is a
      * {@code #class:}, {@code #type:} or {@code #bean:} value, a property 
placeholder, or a class name is left alone.
@@ -169,6 +223,7 @@ final class BeanRefChecks {
         if (content == null) {
             return msgs;
         }
+        msgs.addAll(validateBeanPropertyPlaceholders(content));
         Set<String> declared = new HashSet<>(declaredBeans(content));
         if (external != null) {
             declared.addAll(external.names());
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
index e80081f89f14..a16ae541175f 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
@@ -18,6 +18,7 @@ package org.apache.camel.dsl.jbang.core.commands.ai;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -234,6 +235,8 @@ final class EndpointChecks {
                 }
                 checkRegexOptions(errors, fullUri, i, optionLineMap);
                 checkDynamicDirectory(errors, fullUri, i, eipName);
+                checkSimplePlaceholders(errors, fullUri, i, optionLineMap, 
eipName);
+                checkRequiredPathOptions(errors, fullUri, catalog, i, eipName);
             } catch (Exception e) {
                 // ignore validation errors
             }
@@ -388,6 +391,87 @@ final class EndpointChecks {
                    + "?fileName=${...}), or use toD: with the whole uri, which 
evaluates it per message");
     }
 
+    /** The EIPs whose uri is a Simple expression evaluated per message: 
${...} is right there. */
+    private static final Set<String> DYNAMIC_URI_EIPS = Set.of("toD", "to-d", 
"wireTap", "wire-tap", "enrich", "pollEnrich",
+            "poll-enrich", "recipientList", "recipient-list", "routingSlip", 
"routing-slip", "dynamicRouter", "dynamic-router");
+
+    /**
+     * period=${welcome.period} or period=${properties:welcome.period} on a 
to: or from:: a Simple expression, which an
+     * endpoint option is not; the property placeholder is {{welcome.period}}. 
The runtime fails to bind the option at
+     * startup or on the reload, and camel validate said nothing 
(CAMEL-24857). toD and the other dynamic EIPs evaluate
+     * the uri as Simple first and are left alone.
+     */
+    static void checkSimplePlaceholders(
+            List<String> errors, String fullUri, int uriLineIdx, Map<String, 
Integer> optionLineMap, String eipName) {
+        if (eipName != null && DYNAMIC_URI_EIPS.contains(eipName)) {
+            return;
+        }
+        int q = fullUri.indexOf('?');
+        if (q < 0) {
+            return;
+        }
+        String scheme = fullUri.substring(0, Math.max(0, 
fullUri.indexOf(':')));
+        for (String pair : fullUri.substring(q + 1).split("&")) {
+            int eq = pair.indexOf('=');
+            if (eq < 0) {
+                continue;
+            }
+            String name = pair.substring(0, eq);
+            String value = pair.substring(eq + 1);
+            if (!YamlLines.isPropertyKeyInSimpleSyntax(value)) {
+                continue;
+            }
+            String key = YamlLines.propertyKeyOf(value);
+            errors.add(linePrefix(optionLineMap.getOrDefault(name, 
uriLineIdx)) + scheme + ": " + name + "=" + value
+                       + " is a Simple expression, which an endpoint option is 
not evaluated as: a property placeholder"
+                       + " is written {{key}}, so " + name + ": \"{{" + key + 
"}}\"");
+        }
+    }
+
+    /**
+     * uri: cron with only a schedule under parameters: the required path 
option name is neither in the uri nor among
+     * the parameters; camel run fails with "Option name is required when 
creating endpoint uri with syntax cron:name"
+     * (CAMEL-24858). Says both places it can go.
+     */
+    static void checkRequiredPathOptions(
+            List<String> errors, String fullUri, CamelCatalog catalog, int 
uriLineIdx, String eipName) {
+        int colon = fullUri.indexOf(':');
+        if (colon < 0 || fullUri.contains("{{") || !"from".equals(eipName) && 
!"to".equals(eipName)) {
+            return; // only an endpoint that is created: an intercept pattern 
such as jms* names no destination
+        }
+        String scheme = fullUri.substring(0, colon);
+        int q = fullUri.indexOf('?');
+        String path = q >= 0 ? fullUri.substring(colon + 1, q) : 
fullUri.substring(colon + 1);
+        if (path.startsWith("//") || !path.isEmpty()) {
+            // a path is given: which path option it fills is the component's 
business; an explicit empty authority
+            // (infinispan:// with a custom listener) is a choice, a bare 
scheme with the options under parameters is
+            // the slip this catches
+            return;
+        }
+        Set<String> given = new HashSet<>();
+        if (q >= 0) {
+            for (String pair : fullUri.substring(q + 1).split("&")) {
+                given.add(pair.contains("=") ? pair.substring(0, 
pair.indexOf('=')) : pair);
+            }
+        }
+        try {
+            var model = catalog.componentModel(scheme);
+            if (model == null) {
+                return;
+            }
+            for (var option : model.getEndpointOptions()) {
+                if (option.isRequired() && "path".equals(option.getKind()) && 
!given.contains(option.getName())) {
+                    errors.add(linePrefix(uriLineIdx) + scheme + ": the 
required option '" + option.getName()
+                               + "' is missing (the runtime says 'Option " + 
option.getName() + " is required'): write"
+                               + " it in the uri, uri: " + scheme + ":<" + 
option.getName() + ">, or under parameters"
+                               + " as " + option.getName() + ": <value>");
+                }
+            }
+        } catch (Exception e) {
+            // ignore: a component the catalog does not know is reported 
elsewhere
+        }
+    }
+
     /** A wildcard such as *.txt as the regex .*\\.txt. */
     static String toRegex(String wildcard) {
         StringBuilder sb = new StringBuilder();
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ReloadOutcome.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ReloadOutcome.java
new file mode 100644
index 000000000000..5f5ba2b52a0a
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ReloadOutcome.java
@@ -0,0 +1,144 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.ai;
+
+import java.util.List;
+
+import org.apache.camel.util.StopWatch;
+import org.apache.camel.util.json.JsonObject;
+
+/**
+ * What a dev-mode integration did with a file the agent wrote: the log 
records of the reload, read through
+ * {@link LogFileReader} (CAMEL-24859). A write tool answering "written" while 
the reload failed sends the agent on with
+ * a broken route; the outcome in the answer is what it acts on.
+ */
+public final class ReloadOutcome {
+
+    static final String RELOADED = "Routes reloaded summary";
+    static final String FAILED = "Error reloading routes from file";
+    static final String PROPERTIES = "Reloading properties";
+    static final String REPORT = "did not load";
+
+    private ReloadOutcome() {
+    }
+
+    static boolean isReload(JsonObject r) {
+        String m = r.getStringOrDefault("message", "");
+        return m.contains(RELOADED) || m.contains(FAILED) || 
m.contains(PROPERTIES);
+    }
+
+    static String key(JsonObject r) {
+        return r.getStringOrDefault("time", "") + "|" + 
r.getStringOrDefault("message", "");
+    }
+
+    /** The key of the newest reload record, or null: what a later reload is 
compared against. */
+    public static String latestReloadKey(List<JsonObject> newestFirst) {
+        for (JsonObject r : newestFirst) {
+            if (isReload(r)) {
+                return key(r);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * The outcome in the records newer than the given key: status reloaded, 
failed or properties with the record's
+     * message (and, for a failure, its detail and the validator's report when 
the runtime printed one), or null when no
+     * reload has happened yet.
+     */
+    public static JsonObject classify(List<JsonObject> newestFirst, String 
sinceKey) {
+        for (int i = 0; i < newestFirst.size(); i++) {
+            JsonObject r = newestFirst.get(i);
+            if (!isReload(r)) {
+                continue;
+            }
+            if (sinceKey != null && sinceKey.equals(key(r))) {
+                return null; // the newest reload is the one from before the 
write
+            }
+            String m = r.getStringOrDefault("message", "");
+            JsonObject out = new JsonObject();
+            if (m.contains(FAILED)) {
+                out.put("status", "failed");
+                StringBuilder sb = new StringBuilder(m);
+                String detail = r.getStringOrDefault("detail", "");
+                if (!detail.isEmpty()) {
+                    // the first lines of the cause, not the stack
+                    for (String line : detail.split("\n")) {
+                        if (line.startsWith("\tat ") || line.isBlank()) {
+                            continue;
+                        }
+                        sb.append("\n").append(line);
+                        if (sb.length() > 1500) {
+                            break;
+                        }
+                    }
+                }
+                // the validator's report the runtime logs next to a load 
failure (CAMEL-24851) is older than the
+                // failure record in a newest-first list
+                for (int j = i + 1; j < Math.min(newestFirst.size(), i + 4); 
j++) {
+                    String other = 
newestFirst.get(j).getStringOrDefault("message", "");
+                    if (other.contains(REPORT)) {
+                        sb.append("\n").append(other);
+                        String d = 
newestFirst.get(j).getStringOrDefault("detail", "");
+                        if (!d.isEmpty()) {
+                            sb.append("\n").append(d);
+                        }
+                        break;
+                    }
+                }
+                out.put("message", sb.toString());
+            } else if (m.contains(RELOADED)) {
+                out.put("status", "reloaded");
+                out.put("message", m);
+            } else {
+                out.put("status", "properties");
+                out.put("message", m);
+            }
+            return out;
+        }
+        return null;
+    }
+
+    /** Polls the integration's log for the reload of a just written file, up 
to the timeout. */
+    public static JsonObject await(long pid, String name, String sinceKey, 
long timeoutMillis) {
+        StopWatch watch = new StopWatch();
+        while (watch.taken() < timeoutMillis) {
+            JsonObject outcome = classify(records(pid, name), sinceKey);
+            if (outcome != null) {
+                return outcome;
+            }
+            try {
+                Thread.sleep(500);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                break;
+            }
+        }
+        JsonObject out = new JsonObject();
+        out.put("status", "unknown");
+        out.put("message", "no reload seen in the log within " + timeoutMillis 
/ 1000 + "s: the integration may not run"
+                           + " in dev mode, or the file is not one it watches; 
camel_get_log shows what it did");
+        return out;
+    }
+
+    @SuppressWarnings("unchecked")
+    static List<JsonObject> records(long pid, String name) {
+        JsonObject log = LogFileReader.read(pid, name, 40, null, null);
+        Object lines = log.get("lines");
+        return lines instanceof List ? (List<JsonObject>) lines : List.of();
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
index 9d3f43da61de..9e45dbf8539b 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/YamlLines.java
@@ -16,6 +16,8 @@
  */
 package org.apache.camel.dsl.jbang.core.commands.ai;
 
+import java.util.Set;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import org.apache.camel.util.StringHelper;
@@ -112,6 +114,40 @@ final class YamlLines {
         return unquote(trimmed.substring(trimmed.indexOf(':') + 1).trim());
     }
 
+    /** The roots of a Simple expression: ${body...}, ${header.x}, 
${date:...}: a value evaluated as Simple is fine. */
+    private static final Set<String> SIMPLE_ROOTS = Set.of("body", "bodyAs", 
"mandatoryBodyAs",
+            "originalBody", "header", "headers", "headerAs", 
"exchangeProperty", "exchangeProperties", "exchangePropertyAs",
+            "variable", "variables", "variableAs", "exchange", "camelContext", 
"camelId", "routeId", "routeGroup", "stepId",
+            "id", "messageId", "exchangeId", "messageTimestamp", 
"messageHistory", "threadName", "threadId", "hostname",
+            "null", "date", "bean", "random", "file", "env", "sys", "sysenv", 
"ref", "type", "uuid", "empty", "collate",
+            "exception", "in", "out",
+            "skip", "jsonpath", "xpath", "jq", "iif", "join", "replace", 
"substring", "pretty", "hash", "messageAs",
+            "properties", "propertiesExist");
+
+    private static final Pattern DOTTED_KEY = 
Pattern.compile("^\\$\\{([A-Za-z_][\\w-]*)(?:[.-][\\w-]+)+\\}$");
+
+    /**
+     * Whether a ${...} value is a property key wearing Simple's syntax 
(${welcome.period}, ${properties:x}) rather than
+     * a Simple expression (${body.id}, ${date:now:yyyy}): the first segment 
is not a Simple root (CAMEL-24857).
+     */
+    static boolean isPropertyKeyInSimpleSyntax(String value) {
+        if (value == null) {
+            return false;
+        }
+        if (value.startsWith("${properties:") && value.endsWith("}")) {
+            return true;
+        }
+        Matcher m = DOTTED_KEY.matcher(value);
+        return m.find() && !SIMPLE_ROOTS.contains(m.group(1));
+    }
+
+    /** The key of such a value: welcome.period for ${welcome.period} or 
${properties:welcome.period}. */
+    static String propertyKeyOf(String value) {
+        return value.startsWith("${properties:")
+                ? value.substring("${properties:".length(), value.length() - 1)
+                : value.substring(2, value.length() - 1);
+    }
+
     static String extractEipFromLine(String trimmed) {
         if (trimmed.startsWith("- ")) {
             trimmed = trimmed.substring(2).trim();
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/ReloadOutcomeTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/ReloadOutcomeTest.java
new file mode 100644
index 000000000000..8fc48abb0f91
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/ReloadOutcomeTest.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.ai;
+
+import java.util.List;
+
+import org.apache.camel.util.json.JsonObject;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/** CAMEL-24859: the reload of a written file, read from the log records newer 
than the ones before the write. */
+class ReloadOutcomeTest {
+
+    private static final List<String> BEFORE = List.of(
+            "2026-09-21 10:00:00.001  INFO 42 --- [           main] 
org.apache.camel.main.MainSupport : Apache Camel 4.23.0 is starting",
+            "2026-09-21 10:00:05.000  INFO 42 --- [rReloadStrategy] 
org.apache.camel.support.RouteWatcherReloadStrategy : Routes reloaded summary 
(total:1 started:1)",
+            "2026-09-21 10:00:06.000  INFO 42 --- [ timer://tick] route1 : 
Hello Camel");
+
+    private static final List<String> FAILED = List.of(
+            "2026-09-21 10:00:20.000 ERROR 42 --- [rReloadStrategy] 
org.apache.camel.dsl.jbang.core.commands.ai.YamlLoadFailureReport : The route 
file did not load. camel validate yaml says what to write:",
+            "  a.camel.yaml:",
+            "    cron: the required option 'name' is missing",
+            "2026-09-21 10:00:20.001  WARN 42 --- [rReloadStrategy] 
org.apache.camel.support.FileWatcherResourceReloadStrategy : Error reloading 
routes from file: a.camel.yaml due to: Error constructing YAML node id: 
org.apache.camel.model.FromDefinition. This exception is ignored.",
+            
"org.apache.camel.dsl.yaml.common.exception.YamlDeserializationException: Error 
constructing YAML node id: org.apache.camel.model.FromDefinition",
+            "\tat 
org.apache.camel.dsl.yaml.YamlRoutesBuilderLoader.doConfigure(YamlRoutesBuilderLoader.java:190)",
+            "Caused by: java.lang.IllegalArgumentException: Option name is 
required when creating endpoint uri with syntax cron:name",
+            "\tat 
org.apache.camel.support.component.AbstractApiEndpoint.x(Foo.java:1)");
+
+    private static final List<String> RELOADED = List.of(
+            "2026-09-21 10:00:40.000  INFO 42 --- [rReloadStrategy] 
org.apache.camel.support.RouteWatcherReloadStrategy : Routes reloaded summary 
(total:1 started:1)",
+            "2026-09-21 10:00:41.000  INFO 42 --- [ timer://tick] route1 : 
Hello again");
+
+    @SuppressWarnings("unchecked")
+    private static List<JsonObject> records(List<String>... parts) {
+        List<String> all = new java.util.ArrayList<>();
+        for (List<String> p : parts) {
+            all.addAll(p);
+        }
+        return (List<JsonObject>) (List<?>) List
+                .copyOf(LogFileReader.build(all, 40, null, null, new 
JsonObject()).getCollection("lines"));
+    }
+
+    @Test
+    void theNewestReloadBeforeTheWriteIsTheBaseline() {
+        String since = ReloadOutcome.latestReloadKey(records(BEFORE));
+        assertEquals("10:00:05.000|Routes reloaded summary (total:1 
started:1)", since);
+        assertNull(ReloadOutcome.classify(records(BEFORE), since), "no reload 
since the write yet");
+    }
+
+    @Test
+    void aFailedReloadCarriesTheCauseAndTheValidatorsReport() {
+        String since = ReloadOutcome.latestReloadKey(records(BEFORE));
+        JsonObject out = ReloadOutcome.classify(records(BEFORE, FAILED), 
since);
+        assertEquals("failed", out.getString("status"));
+        String m = out.getString("message");
+        assertTrue(m.startsWith("Error reloading routes from file: 
a.camel.yaml"), m);
+        assertTrue(m.contains("Caused by: java.lang.IllegalArgumentException: 
Option name is required"), m);
+        assertTrue(m.contains("camel validate yaml says what to write"), "the 
runtime's report is carried along: " + m);
+        assertTrue(m.contains("the required option 'name' is missing"), m);
+        assertTrue(!m.contains("\tat "), "no stack frames: " + m);
+    }
+
+    @Test
+    void aPropertiesReloadIsReportedAsSuch() {
+        List<String> props = List.of(
+                "2026-09-21 10:00:30.000  INFO 42 --- [rReloadStrategy] 
org.apache.camel.main.DefaultConfigurationConfigurer : Reloading properties: 
file:application.properties");
+        String since = ReloadOutcome.latestReloadKey(records(BEFORE));
+        JsonObject out = ReloadOutcome.classify(records(BEFORE, props), since);
+        assertEquals("properties", out.getString("status"));
+        assertEquals("Reloading properties: file:application.properties", 
out.getString("message"));
+    }
+
+    @Test
+    void aReloadAfterTheFailureIsReloaded() {
+        String since = ReloadOutcome.latestReloadKey(records(BEFORE));
+        JsonObject out = ReloadOutcome.classify(records(BEFORE, FAILED, 
RELOADED), since);
+        assertEquals("reloaded", out.getString("status"));
+        assertEquals("Routes reloaded summary (total:1 started:1)", 
out.getString("message"));
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorPlaceholderTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorPlaceholderTest.java
new file mode 100644
index 000000000000..36822c18c4fd
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorPlaceholderTest.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.ai;
+
+import java.util.List;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * CAMEL-24857 and CAMEL-24858: a ${...} placeholder where {{...}} is meant 
(an endpoint option, a bean property), and a
+ * required endpoint path option that is missing.
+ */
+class SourceValidatorPlaceholderTest {
+
+    private static final CamelCatalog catalog = new DefaultCamelCatalog();
+
+    @Test
+    void aSimplePlaceholderInAnEndpointOptionSaysToWriteAPropertyPlaceholder() 
{
+        String yaml = """
+                - route:
+                    from:
+                      uri: timer
+                      parameters:
+                        timerName: welcome
+                        period: "${welcome.period}"
+                      steps:
+                        - to:
+                            uri: "log:done?level=${properties:log.level}"
+                """;
+        List<String> errors = SourceValidator.validateYamlEndpoints(yaml, 
catalog);
+        assertThat(errors).anyMatch(e -> e.startsWith("Line 6: timer: 
period=${welcome.period} is a Simple expression")
+                && e.endsWith("so period: \"{{welcome.period}}\""));
+        assertThat(errors).anyMatch(e -> e.startsWith("Line 9: log: 
level=${properties:log.level} is a Simple expression")
+                && e.endsWith("so level: \"{{log.level}}\""));
+    }
+
+    @Test
+    void aDynamicEipAndAPropertyPlaceholderAreFine() {
+        String yaml = """
+                - route:
+                    from:
+                      uri: timer
+                      parameters:
+                        timerName: welcome
+                        period: "{{welcome.period}}"
+                      steps:
+                        - toD:
+                            uri: "log:${header.target}?level=INFO"
+                """;
+        assertThat(SourceValidator.validateYamlEndpoints(yaml, 
catalog)).noneMatch(e -> e.contains("Simple expression"));
+    }
+
+    @Test
+    void aMissingRequiredPathOptionIsReportedWithBothPlaces() {
+        String yaml = """
+                - route:
+                    from:
+                      uri: cron
+                      parameters:
+                        schedule: 0/10 * * * * ?
+                      steps:
+                        - to:
+                            uri: log:done
+                """;
+        List<String> errors = SourceValidator.validateYamlEndpoints(yaml, 
catalog);
+        assertThat(errors).anyMatch(e -> e.startsWith("Line 3: cron: the 
required option 'name' is missing")
+                && e.contains("uri: cron:<name>") && e.contains("name: 
<value>"));
+        // given in the uri, or as a parameter: fine
+        assertThat(SourceValidator.validateYamlEndpoints(yaml.replace("uri: 
cron", "uri: cron:report"), catalog))
+                .noneMatch(e -> e.contains("required option"));
+        
assertThat(SourceValidator.validateYamlEndpoints(yaml.replace("schedule: 0/10", 
"name: report\n        schedule: 0/10"),
+                catalog))
+                .noneMatch(e -> e.contains("required option"));
+    }
+
+    @Test
+    void aSimplePlaceholderInABeanPropertySaysToWriteAPropertyPlaceholder() {
+        String yaml = """
+                - beans:
+                    - name: orderNumber
+                      type: "camel.example.OrderNumber"
+                      properties:
+                        start: ${order.first-number}
+                        label: "{{order.label}}"
+                """;
+        List<String> msgs = 
BeanRefChecks.validateBeanPropertyPlaceholders(yaml);
+        assertThat(msgs).hasSize(1);
+        assertThat(msgs.get(0)).startsWith("Line 5: start: 
${order.first-number} is a Simple expression")
+                .endsWith("so start: \"{{order.first-number}}\"");
+    }
+}

Reply via email to