Skip to content

CAMEL-26484: camel-jbang - AI tools: a JsonPath or SpEL filter in Simple, a jsonpath text that is not a path, {id} for a path parameter and json as a language are caught at write time - #28849

Merged
davsclaus merged 2 commits into
mainfrom
CAMEL-26484
Nov 2, 2026

Conversation

@davsclaus

Copy link
Copy Markdown
Contributor

https://issues.apache.org/jira/browse/CAMEL-26484

In the r25 run of the local-model benchmark (qwen3.6 35B on Ollama), contracts-rest-code-first step 1 passed in 2 of 7 rounds, against 5 of 7 in r24. The step asks to answer one order by its id, or 404. The validator accepted routes that then failed on every request:

  • ${bodyAs(List).?[orderId == '${header.id}']} and ${body[?(@.orderId == '${header.id}')]}: a SpEL or JsonPath filter inside Simple, which Simple reads as a method or a key.
  • jsonpath $[?(...)] {resultType: java.util.Map}: the model copied the words of the List message into the text of the path (InvalidPathException).
  • jsonpath $[?(@.orderId == '{id}')]: the text {id}, not the path parameter, so every id got 404.
  • The JSON answer of the 404 under json:: it was told "did you mean 'js'?" 4 times, then wrote it as jsonpath, which failed on a null body.

What changes, at write time

  • A JsonPath or SpEL filter inside a Simple function is reported, with the jsonpath step that picks the element, as YAML lines.
  • The text of a jsonpath expression is read as a path. Text after the path, a bracket or a quote that is not closed, and a JSON text are reported, with the lines to write.
    • The JsonPath library is not added: with the JSON parser it needs, it would be four more jars in the CLI.
    • So the check is light, and it reports only what the JsonPath compiler refuses.
  • {id} is reported when a REST operation with the path parameter id reaches the route. This covers a simple, constant or jsonpath text, and a log message. The message says it is the literal text, and that the path parameter is the header ${header.id}.
    • The REST operations of the other files of the project count too, and so does an OpenAPI specification a file binds to.
  • json: under expression: gets the answer that json is a data format, not a language. It gives the JSON text with constant, or with simple when it has ${...}. This replaces "did you mean 'js'?".
  • The List message (CAMEL-26386: camel-yaml-dsl-validator - Body type checks: a field read on the List of a JSON array file, a field read after a marshal of an unknown body, a variableSend of a header, and a setBody that emptied the body #28636) gives the jsonpath step as YAML lines, with expression: and resultType: as two keys. When the route's own path parameter matches the field, it uses that header.

Tests

  • camel-yaml-dsl-validator: 339 tests pass.
  • The AI tool tests in camel-jbang-core: 825 tests pass, with 1 skipped as before.
  • The new checks report nothing on these:
    • the reference files of the benchmark steps;
    • the YAML examples of the simple, jsonpath and REST DSL docs (66);
    • the 1238 right final versions of the r24 and r25 runs.
  • Every r25 write that the validator accepted and that then failed at runtime is now refused.

🤖 Generated with Claude Code

https://claude.ai/code/session_016Y4dDi5iQNgtHrDdz2NpwF

@davsclaus davsclaus added this to the 4.24.0 milestone Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions github-actions Bot added the dsl label Nov 2, 2026
@github-actions

github-actions Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • dsl/camel-jbang/camel-jbang-core
  • dsl/camel-yaml-dsl/camel-yaml-dsl-validator

🔁 2 tests passed only after a retry on JDK 21 (2 retried attempts)

Recovered flaky tests on JDK 21 (2)
Module Test Failed attempts First failure
dsl/camel-jbang/camel-jbang-plugin-tui AiCliCommandExecutorTest.productionInvokerRestoresPrinterAndDoesNotQuitMain 1 unexpected exit code, output was: java.lang.IllegalStateException: Terminal has been closed at org.jline.terminal.impl.AbstractTerminal.checkClosed(AbstractTerminal.java:1172) at org.jline.terminal.impl.LineDisciplineTerminal.writer(LineDisciplineTerminal.java:212) at org.apache.camel.dsl.jbang.core.commands.tui.ShellPanel$2.println(ShellPanel.java:507) at org.apache.camel.dsl.jbang.run.common.Printer.printDiagnostic(Printer.java:47) at org.apache.camel.dsl.jbang.run.common.Printer.printErr(Printer.java:54) at org.apache.camel.dsl.jbang.core.commands.exceptionhandler.CliExecutionExceptionHandler.handle(CliExecutionExceptionHandler.java:77) at org.apache.camel.dsl.jbang.core.commands.exceptionhandler.CliExecutionExceptionHandler.handleExecutionException(CliExecutionExceptionHandler.java:63) at picocli.CommandLine.execute(CommandLine.java:2184) at org.apache.camel.dsl.jbang.core.commands.CliRunner.execute(CliRunner.java:182) at org.apache.camel.dsl.jbang.core.commands.CliRunner.run(CliRunner.java:116) at org.apache.camel.dsl.jbang.core.commands.CliRunner.run(CliRunner.java:97) at org.apache.camel.dsl.jbang.core.commands.tui.AiCliCommandExecutor.invoke(AiCliCommandExecutor.java:140) at org.apache.camel.dsl.jbang.core.commands.tui.AiCliCommandExecutor.execute(AiCliCommandExecutor.java:111) at org.apache.camel.dsl.jbang.core.commands.tui.AiCliCommandExecutor.lambda$executeAsync$0(AiCliCommandExecutor.java:62) at java.base/java.lang.Thread.run(Thread.java:1583) ==> expected: <0> but was: <70>
dsl/camel-jbang/camel-jbang-plugin-tui MonitorStartupTest.browserOnlyServesUntilTheServerStops 1 Connection refused (connect failed)

ℹ️ These tests did not fail the build. Surefire retried them and they passed.
Retries are enabled project-wide by surefire.rerunFailingTestsCount in parent/pom.xml.


🔬 Scalpel shadow comparison — Scalpel: 7 of 713 tested, 8 compile-only — current: 7 all tested

Maveniverse Scalpel detected 7 affected modules (current approach: 7).

Skip-tests mode would test 7 modules (2 direct + 6 downstream), skip tests for 8 (generated code, meta-modules)

Modules Scalpel would test (7)
  • camel-jbang-mcp ← downstream of org.apache.camel:camel-jbang-core
  • camel-jbang-plugin-mcp ← downstream of org.apache.camel:camel-jbang-core
  • camel-jbang-plugin-tui ← downstream of org.apache.camel:camel-jbang-core
  • camel-jbang-plugin-validate ← downstream of org.apache.camel:camel-jbang-core
  • camel-launcher-container ← downstream of org.apache.camel:camel-launcher
  • camel-yaml-dsl-validator ← dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/JsonPathText.java, dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/ProjectFacts.java, dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/RouteGraph.java, dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/SchemaHints.java, dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/TextBodyFlow.java, dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java, dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java, dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorPropertyHintTest.java
  • camel-yaml-dsl-validator-maven-plugin ← downstream of org.apache.camel:camel-yaml-dsl-validator
Modules with tests skipped (8)
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-launcher
  • coverage

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

⚠️ Some tests are disabled on GitHub Actions (@DisabledIfSystemProperty(named = "ci.env.name")) and require manual verification:

  • dsl/camel-jbang/camel-jbang-core: 2 test(s) disabled on GitHub Actions

💡 Manual integration tests recommended:

You modified dsl/camel-jbang/camel-jbang-core. The related integration tests in dsl/camel-jbang/camel-jbang-it are excluded from CI. Consider running them manually:

mvn verify -f dsl/camel-jbang/camel-jbang-it -Djbang-it-test
All tested modules (16 modules, 2m 20s total)

Total reactor time: 2m 20s

Module Duration Status
Camel :: JBang :: MCP 44.9s SUCCESS
Camel :: JBang :: Plugin :: Validate 27.7s SUCCESS
Camel :: YAML DSL :: Validator 24.6s SUCCESS
Camel :: JBang :: Plugin :: Kubernetes 12.9s SUCCESS
Camel :: JBang :: Plugin :: Testing 12.5s SUCCESS
Camel :: JBang :: Plugin :: Generate 5.2s SUCCESS
Camel :: Coverage 3.9s SUCCESS
Camel :: YAML DSL :: Validator Maven Plugin 3.0s SUCCESS
Camel :: JBang :: Plugin :: MCP 1.8s SUCCESS
Camel :: JBang :: Plugin :: Edit 1.4s SUCCESS
Camel :: JBang :: Integration tests 0.8s SUCCESS
Camel :: JBang :: Main 0.7s SUCCESS
Camel :: Launcher :: Container 0.7s SUCCESS
Camel :: JBang :: Core n/a
Camel :: JBang :: Plugin :: TUI n/a
Camel :: Launcher n/a

Top 20 slowest modules:

  • Camel :: JBang :: MCP (44.9s)
  • Camel :: JBang :: Plugin :: Validate (27.7s)
  • Camel :: YAML DSL :: Validator (24.6s)
  • Camel :: JBang :: Plugin :: Kubernetes (12.9s)
  • Camel :: JBang :: Plugin :: Testing (12.5s)
  • Camel :: JBang :: Plugin :: Generate (5.2s)
  • Camel :: Coverage (3.9s)
  • Camel :: YAML DSL :: Validator Maven Plugin (3.0s)
  • Camel :: JBang :: Plugin :: MCP (1.8s)
  • Camel :: JBang :: Plugin :: Edit (1.4s)
  • Camel :: JBang :: Integration tests (0.8s)
  • Camel :: JBang :: Main (0.7s)
  • Camel :: Launcher :: Container (0.7s)

⚙️ View full build and test results

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — well-structured enhancement adding write-time validation for four AI-model failure modes: REST verbs, path parameters, OpenAPI verbs, and URI JSON path. Thorough test coverage and defensive null handling throughout.

Minor notes (non-blocking):

  • REST_VERBS and PATH_PARAMETER constants are defined identically in three files (RouteGraph, ValidatorFacts, OpenApiVerbs). Worth extracting to a shared constants class to avoid drift.
  • The Walk record now has 15 parameters — approaching builder-pattern territory for readability.
  • In ValidatorFacts, uri.indexOf('?') is computed twice in the same expression — assign to a local variable first.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

…ple, a jsonpath text that is not a path, {id} for a path parameter and json as a language are caught at write time

In the r25 run of the local-model benchmark, rest-code-first step 1 (answer
one order by its id, or 404) passed in 2 of 7 rounds, against 5 of 7 in r24.
The validator accepted routes that then failed on every request:
- ${bodyAs(List).?[orderId == '${header.id}']} and
  ${body[?(@.orderId == '${header.id}')]}: a SpEL or JsonPath filter inside
  Simple, which Simple reads as a method or a key.
- jsonpath "$[?(...)] {resultType: java.util.Map}": the words of the List
  message copied into the text of the path (InvalidPathException).
- jsonpath "$[?(@.orderId == '{id}')]": the text {id}, not the path
  parameter, so every id got 404.
- The JSON answer of the 404 under json:, told "did you mean 'js'?" 4 times,
  then written as jsonpath, which failed on a null body.

What changed, at write time:
- A JsonPath or SpEL filter inside a Simple function is reported, with the
  jsonpath step that picks the element, as YAML lines.
- The text of a jsonpath expression is read as a path. Text after the path, a
  bracket or a quote that is not closed, and a JSON text are reported, with
  the lines to write. The JsonPath library is not added: with the JSON parser
  it needs, it is four more jars in the CLI. So the check is light, and it
  reports only what the JsonPath compiler refuses.
- {id} in a simple, constant or jsonpath text, or in a log message, is
  reported when a REST operation with the path parameter id reaches the
  route: it is the literal text, and the path parameter is the header
  ${header.id}. The REST operations of the other files of the project, and of
  an OpenAPI specification a file binds to, count too.
- json: under expression: says that json is a data format, not a language,
  and gives the JSON text with constant (simple when it has ${...}) in place
  of "did you mean 'js'?".
- The List message gives the jsonpath step as YAML lines, with expression:
  and resultType: as two keys, and with the header of the route's own path
  parameter when its name matches the field.

The new checks report nothing on the reference files of the benchmark steps,
on the YAML examples of the simple, jsonpath and REST DSL docs, or on the
1238 right final versions of the r24 and r25 runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Y4dDi5iQNgtHrDdz2NpwF

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice addition to camel-jbang AI tooling — the write-time validation catches several easy-to-miss mistakes (JsonPath text vs path, SpEL/Simple confusion, path-parameter hygiene) that would otherwise silently produce bad routes. Two minor style observations:

Double indexOf('?') call (ValidatorFacts.java:174)

return uri == null ? null : uri.indexOf('?') > 0 ? uri.substring(0, uri.indexOf('?')) : uri.trim();

uri.indexOf('?') is computed twice. Extract it:

if (uri == null) return null;
int q = uri.indexOf('?');
return q > 0 ? uri.substring(0, q) : uri.trim();

Duplicated constants across three files

PATH_PARAMETER (Pattern.compile("\\{([A-Za-z_][\\w.-]*)}") ) appears identically in OpenApiVerbs, ValidatorFacts, and RouteGraph. REST_VERBS (List.of("get","post","put","delete","patch","head")) appears in both ValidatorFacts and RouteGraph. Worth extracting to a small shared constants class (or promoting to the existing RouteGraph as package-private statics) to avoid the three definitions drifting independently.

These are low-severity nits — the logic is sound.

…e ai package, and the ? of a uri found once

From the review: OpenApiVerbs reads the PATH_PARAMETER of ValidatorFacts
instead of a copy of its own, and the uri and directory helpers compute
indexOf('?') once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Y4dDi5iQNgtHrDdz2NpwF
@davsclaus

Copy link
Copy Markdown
Contributor Author

Thanks, both done in the last commit:

  • indexOf('?') is computed once, in the uri helper and in the directory helper of ValidatorFacts.
  • OpenApiVerbs now reads ValidatorFacts.PATH_PARAMETER, so the ai package has one copy.

RouteGraph keeps its own PATH_PARAMETER and REST_VERBS. It is in the camel-yaml-dsl-validator module, and making its constants public just to share one regex across modules does not seem worth the wider API.

Validator tests: 345 pass. The AI tool tests of camel-jbang-core: 837 pass.

Claude Code on behalf of davsclaus

@oscerd oscerd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Source-based (the JDK21 build (21, false) check is still queued in CI, not failed — worth letting it finish before merge). This continues the same static-analysis approach as the sibling authoring-tool PRs and keeps to it: JsonPathText, SimpleChecks, the path-parameter and OpenAPI-verb checks all parse with snakeyaml SafeConstructor and otherwise work over the parsed structure plus regex, with no Runtime/ProcessBuilder, Class.forName/loadClass, URL/Socket/HTTP, CamelContext or expression evaluation. The "jsonpath evaluates before it compiles" / "constant vs simple evaluates it" lines are the checks describing the runtime's behavior to the author, not performing it — so this stays a read-only source analysis.

Coverage is solid and again tests both directions: a JsonPath/SpEL filter written inside a Simple function is reported and tied back to the right step (aFilterOfAnotherLanguageInAFunctionIsReportedWithTheJsonPathStep), while whatIsNotAFilterOfAnotherLanguageIsNotReported guards against over-flagging; json used as a language is identified as a data format; and the {id} path-parameter handling is covered for literal, REST and project-wide cases. Consistent and safe. LGTM.

Claude Code on behalf of oscerd

@davsclaus
davsclaus merged commit 41b2627 into main Nov 2, 2026
6 checks passed
davsclaus added a commit that referenced this pull request Nov 2, 2026
…m source (#28886)

camel-jbang-core calls the API of camel-yaml-dsl-validator. When a change
on main touches both, the workflow compiled camel-jbang-core from source
against the published validator snapshot and failed until the next deploy
(seen on #28885 after #28849).

Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants