Guidelines for AI agents working on this codebase.
Apache Camel is an integration framework supporting routing rules in Java, XML and YAML DSLs.
These rules apply to ALL AI agents working on this codebase.
gh api /user --jq '.login' to get the authenticated GitHub username.Co-authored-by). For Claude Code, enable this via the attribution settings.pr-cleanup-branches.yml workflow deletes non-protected branches when their PR is closed, but using a fork avoids the problem entirely.pr-cleanup-branches.yml workflow handles this automatically for branches pushed directly to apache/camel, but agents using forks must delete their remote branch manually.An agent MUST ONLY pick up Unassigned JIRA tickets.
If a ticket is already assigned to a human, the agent must not reassign it or work on it.
Before starting work, the agent must assign the ticket to its operator and transition it to “In Progress”. The CAMEL workflow (ASF JIRA) uses these numeric transition IDs:
4 → Start Progress (In Progress)5 → Resolve Issue (Resolved)2 → Close Issue (Closed)Still query the issue's /transitions endpoint before firing one of these IDs. Availability depends on both the current state and on the caller — 4 is only offered once the issue is assigned to you, which is why the assign step above comes first. The list saves a lookup, it does not replace the availability check.
Before closing a ticket, always set the correct fixVersions field. Note: fixVersions cannot be set on an already-closed issue — set it before closing, or reopen/set/close if needed.
When pushing new commits to a PR, always update the PR description (and title if needed) to reflect the current state of the changeset. PRs evolve across commits — the description must stay accurate and complete. Use gh pr edit --title "..." --body "..." after each push.
When a PR is ready for review (not in draft), always identify and request reviews from the most relevant committers. Do NOT request reviewers on draft PRs — wait until the PR is marked ready for review.
git log --format='%an' --since='1 year' -- <affected-files> | sort | uniq -c | sort -rn | head -10 to find who has been most active on the affected files.git blame on key modified files to identify who wrote the code being changed.gh pr edit --add-reviewer.main. See “Backport PRs” below.A backport PR that cherry-picks a commit from a PR already reviewed and merged on main onto an older LTS maintenance branch (e.g. camel-4.14.x, camel-4.18.x) does not need a fresh review — the code change already went through human review on the PR it was backported from.
gh pr diff <backport-pr> --repo apache/camel > /tmp/backport.diff gh pr diff <original-pr> --repo apache/camel > /tmp/original.diff diff /tmp/backport.diff /tmp/original.diffAny semantic difference means the backport diverged from the original — treat it as a normal PR and request review.
When an AI agent is doing a review:
When merging a PR, an agent MUST perform the following steps in order:
Derive the milestone from the target branch:
<version> from the root pom.xml on the PR's target branch (e.g., main, camel-4.18.x).-SNAPSHOT suffix to get the milestone name (e.g., 4.24.0-SNAPSHOT → 4.23.0).Assign the milestone:
gh pr edit <PR> --milestone <version>.gh api repos/{owner}/{repo}/milestones -f title="<version>".fixVersions on the corresponding JIRA issue to the same version. Note: fixVersions cannot be set on an already-closed issue — always set it before closing.Assign the PR and JIRA issue to the contributor:
gh pr view <PR> --json author --jq '.author.login'.gh pr edit <PR> --add-assignee <author>.Categorize the PR with labels:
bug — for Camel end user facing bug fixes (JIRA type: Bug) - any CI, build system or internal project managed should be categorized as task.enhancement — for improvements and new features (JIRA type: Improvement, New Feature)documentation — for documentation-only changes (JIRA type: Documentation)task — for chores, refactoring, build changes (JIRA type: Task)dependency — for dependency upgradestest — for test-only changes (JIRA type: Test)gh pr edit <PR> --add-label <category>.Merge the PR:
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>).gh pr merge <PR> --squash (or --merge / --rebase as appropriate).Close the JIRA issue:
fixVersions is already set from step 2). The transition IDs are listed under “JIRA Ticket Ownership” above.Clean up the branch:
docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_XX.adoc). This includes: changed defaults, new auto-detection behavior, removed or renamed options, changed header names or values, API/SPI signature changes, removed or deprecated components, migrated libraries, and renamed documentation pages. The upgrade guide is for migration only — do NOT add new features to it. For backported changes, the upgrade guide entry must be added on the main branch (not on the maintenance branch where the fix is backported).mvn formatter:format impsort:sort) before pushing.Prefer AssertJ assertions over JUnit assertions in test code. AssertJ is already available as a test dependency in the project and provides more readable, fluent assertions with better failure messages.
Examples:
// Preferred — AssertJ: assertThat(result).isEqualTo("expected"); assertThat(list).hasSize(3).contains("a", "b"); assertThat(exception).isInstanceOf(IOException.class).hasMessageContaining("timeout"); assertThat(exchange.getIn().getBody(String.class)).startsWith("Hello"); // Avoid — JUnit: assertEquals("expected", result); assertEquals(3, list.size()); assertTrue(list.contains("a"));
Rules:
assertThat(...)) instead of JUnit assertions (assertEquals, assertTrue, assertFalse, assertNotNull, etc.).MockEndpoint.assertIsSatisfied() and other Camel-specific assertion methods are NOT JUnit assertions — keep using them as-is.Do NOT use Thread.sleep() in test code. It leads to flaky, slow, and non-deterministic tests. Use the Awaitility library instead, which is already available as a test dependency in the project.
Example — waiting for a route to be registered:
import static org.awaitility.Awaitility.await; await().atMost(20, TimeUnit.SECONDS) .untilAsserted(() -> assertEquals(1, context.getRoutes().size()));
MockEndpoint tests — prefer built-in timed assertions:
When the wait condition is “mock expectations are met”, use MockEndpoint's native timed assertion instead of wrapping with Awaitility. It is latch-based (more efficient than polling) and requires no external dependency:
// Preferred — native, latch-based, returns as soon as expectations are met: MockEndpoint.assertIsSatisfied(context, 10, TimeUnit.SECONDS); // Also available on a single endpoint: mock.setResultWaitTime(TimeUnit.SECONDS.toMillis(10)); mock.assertIsSatisfied(); // DO NOT wrap MockEndpoint assertions with Awaitility — it polls a mechanism that already waits: // await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> MockEndpoint.assertIsSatisfied(context));
Note: MockEndpoint.assertIsSatisfied(context) (no timeout argument) already waits up to 10 seconds internally — waitForCompleteLatch defaults to 10 000 ms when resultWaitTime is not set. The timed overload is only needed when you want a different timeout.
Use Awaitility only when waiting on a condition that MockEndpoint cannot express natively, such as waiting for a specific received count mid-test before performing the next action:
// Awaitility IS appropriate here — no MockEndpoint API for "wait until N received" without asserting: await().atMost(10, TimeUnit.SECONDS).until(() -> mock.getReceivedCounter() >= 2);
Rules:
Thread.sleep() calls.Thread.sleep(), migrate it to MockEndpoint's timed assertions (for mock-based waits) or Awaitility (for other conditions).MockEndpoint.assertIsSatisfied() with Awaitility — it already waits internally via a CountDownLatch. Wrapping it with untilAsserted adds polling on top of a mechanism that already blocks, which is redundant and less efficient.atMost timeout to avoid hanging builds.untilAsserted or until with a clear predicate — do not replace a sleep with a busy-wait loop.Before implementing a fix for a JIRA issue, thoroughly investigate the issue's validity and context. Camel is a large, long-lived project — code often looks “wrong” but exists for good reasons. Do NOT jump straight to implementation after reading the issue description and the current code.
Required investigation steps:
git log --oneline <file> and git blame <file> on the affected code. Read the commit messages and linked JIRA tickets for prior changes to understand why the code is written the way it is.design/ directory for design docs (.adoc files) that may explain architectural decisions in the affected area. Key documents by area:design/security.adocdesign/tracing.adocdesign/mdc.adocdesign/headers.adocdesign/cross-cutting-binding.adocLwJavaParser, its safety rules): design/java-dsl-parser.adoccamel-opentelemetry2 replacing camel-opentelemetry), understand why the replacement was made and what was intentionally changed vs. accidentally omitted.Present your findings to the operator before implementing. Flag any risks, ambiguities, or cases where the issue may be invalid or the proposed approach may conflict with prior decisions.
AI agents have a training data cutoff and may not know about recent releases, API changes, or deprecations in external projects. Never make authoritative claims about external project state based solely on training knowledge.
When reviewing PRs, apply the same investigative rigor:
git log and git blame on modified files to see if the change conflicts with prior intentional decisions.design/*.adoc) related to the affected area (see the area-to-document mapping in “Issue Investigation” above).When writing or modifying .adoc documentation:
xref: for internal links, never external https://camel.apache.org/... URLs. Example: xref:manual::camel-jbang.adoc[Camel CLI] instead of https://camel.apache.org/manual/camel-jbang.html[Camel CLI].#_my_section) using the components:: prefix, verify that the target section exists in the current released version, not just on main. The components:: prefix resolves to the latest released version, so anchors that only exist on main will produce broken links. Either omit the fragment or use a version-aware reference.xref: links and anchors resolve correctly, especially cross-component references that may span versions.[[anchor]] blocks before a heading. AsciiDoc already auto-generates an id from the heading text (prefixed with _, e.g. === Structured error exchange properties becomes #_structured_error_exchange_properties). An explicit [[structured_error_exchange_properties]] anchor sets the id without that prefix, so it silently diverges from the id every other xref: in the codebase expects and breaks the website link checker. Only add an explicit anchor when a stable id is needed that must survive a heading rename — never as a matter of habit.src/main/docs/*.adoc also requires regenerating and committing the mirrored copy under catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/. CI’s “uncommitted changes” check fails otherwise — this applies even to small doc-only edits like removing an anchor, not just code-driven metadata changes.Camel has a documented threat model that defines who is trusted, where the trust boundaries sit, what counts as a framework vulnerability, and what is operator responsibility. The canonical document is docs/user-manual/modules/ROOT/pages/security-model.adoc. Use it as the reference when triaging security reports, deciding whether a finding warrants a CVE, or reviewing a security-sensitive PR.
For the vulnerability reporting convention, SECURITY.md at the repository root is the entry point GitHub and security tooling expect. It points to the threat model above for scope and to the ASF process for private disclosure. An agent that discovers or is handed a suspected vulnerability MUST NOT open a public issue, PR, or mailing-list post about it — follow the private process in SECURITY.md and stop.
.bean() / .process(), evaluate arbitrary expressions in simple / groovy / jexl / mvel / xpath, and configure every component option. Code execution by a route author is by design and is not a vulnerability.The fundamental trust boundary is between the route plus its configuration (trusted) and the data flowing through the route (untrusted). The framework must not turn untrusted data into code execution, file read, request forgery, or auth bypass on its own.
Reports that demonstrate untrusted input crossing a trust boundary the framework should have held — in a default or reasonably-expected configuration — are in scope. Concrete classes the PMC has historically accepted:
ObjectInputStream in consumers, type converters, aggregation repositories, key stores).HeaderFilterStrategy.LwJavaParser, the project overview, the TUI Architecture view, the camel_project_overview MCP tool) running code from the source, loading its classes, resolving its secrets, reaching the network or not finishing. Running the routes is the trusted route-author case.The following are explicitly not framework vulnerabilities and will be closed as such:
.bean(), .process(), Runtime.exec(), or evaluating simple / groovy on untrusted input. Route code is trusted.allowJavaSerializedObject=true, transferException=true, trustAllCertificates=true, hostnameVerificationEnabled=false, or selecting an ObjectInputStream-using data format.throttle, circuitBreaker, resilience4j, JVM limits.camel-management, the developer console, camel-jolokia or JMX on a public network.SELECT, and a code-running expression language such as Groovy. A tool breaking that contract is in scope. See “Developer tools” in the security model.When reviewing or recommending a deployment, surface the following:
camel.main.profile = prod so the default for secret / insecure:ssl / insecure:serialization / insecure:dev is fail (see design/security.adoc).SSLContextParameters (the JSSE Utility); never trustAllCertificates in production.Camel*, org.apache.camel.*) from messages arriving from untrusted producers using removeHeaders("Camel*") before any dispatching processor.camel-management, the developer console, camel-jolokia and JMX on a trusted network only.When reviewing a PR that touches a consumer, type converter, aggregation repository, data format, parser, or anything that handles @UriParam security knobs:
HeaderFilterStrategy that blocks Camel* / camel* / org.apache.camel.* case-insensitively? The header-injection family (CVE-2025-27636 and five follow-ons) recurred precisely because new consumers shipped without it.ObjectInputStream.readObject() (directly or via Hessian/Castor/XStream) without an ObjectInputFilter? Five sequential CVEs (CVE-2024-22369, 23114, 2026-25747, 27172, 40858) accepted this exact pattern in aggregation repositories.@UriParam control a security-relevant default? If so, mark it with secret = true for secrets or security = "insecure:ssl" / "insecure:serialization" / "insecure:dev" for risky flags (see the Annotations subsection further down).RouteBuilder, BuilderSupport, RouteConfigurationBuilder) or the static DSL (Builder, PredicateBuilder, AggregationStrategies)? LwJavaParserSecurityTest then fails on purpose: LwJavaParser can call these methods while reading an unvetted source. Check that the new method only builds the model (no CamelContext, files, class loading or network) before regenerating replay-builder-methods.txt with -Dcamel.java.in.updateSurface=true. See design/java-dsl-parser.adoc.camel/ ├── core/ # Core modules (camel-api, camel-core, camel-support, etc.) ├── components/ # 300+ components (kafka, aws, http, etc.) ├── dsl/ # DSLs (jbang, yaml-dsl, endpointdsl) ├── test-infra/ # Testcontainers-based test services ├── catalog/ # Component metadata ├── tooling/ # Maven plugins ├── archetypes/ # Maven archetypes ├── tests/ # Integration tests └── docs/ # AsciiDoc docs
For project build commands, code style restrictions and other standards, check .oss-ai-helper-rules/project-standards.md.
mvn clean install -Dquickly # fast build, no tests mvn clean install # full build mvn clean install -pl components/camel-kafka -am # single module mvn formatter:format && mvn impsort:sort # format code mvn clean install -Psourcecheck # verify style
mvn test # unit tests mvn verify -Pit # integration tests
Test infra usage:
@RegisterExtension static KafkaService service = KafkaServiceFactory.createService();
camel-<name>/
├── pom.xml
└── src/
├── main/java/org/apache/camel/component/<name>/
│ ├── <Name>Component.java
│ ├── <Name>Endpoint.java
│ ├── <Name>Producer.java
│ └── <Name>Consumer.java
├── main/docs/
├── generated/
└── test/java/
Classes:
<Name>Component extends DefaultComponent<Name>Endpoint extends DefaultEndpoint<Name>Producer extends DefaultProducer<Name>Consumer extends DefaultConsumer*Test.java (JUnit 5)Packages:
org.apache.camel.component.<name>org.apache.camel.<module>Deprecation:
(deprecated) suffix to <name> in pom.xml: <name>Camel :: MyComponent (deprecated)</name>(deprecated) suffix to the doc page title in src/main/docs/*.adoc@Deprecated to Java classesdocs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_XX.adoc)Annotations:
@UriPath for path params@UriParam for query paramsdescription for docssecret = true on @UriParam or @Metadata (passwords, tokens, API keys)trustAllCertificates, allowJavaSerializedObject), add security = "insecure:ssl" / "insecure:serialization" / "insecure:dev" on @UriParam. See design/security.adoc for categories and rationale.Import Style:
import statement and use the simple class name (e.g., write List not java.util.List).java.util.Date and java.sql.Date), import the most-used one and qualify the other.src/generated/) is excluded from this rule.rewrite-maven-plugin). CI will fail if uncommitted FQCN changes are detected after the build.Javadoc @since Tags:
core/camel-api MUST include a @since X.Y Javadoc tag indicating the Camel version when they are introduced.core/camel-api MUST include a @since X.Y Javadoc tag on each new method.@since 4.21 if the current SNAPSHOT is 4.21.0).@since as the last Javadoc tag, after @param, @return, @throws, etc.No extra config needed.
You must register the component in MojoHelper:
File: tooling/maven/camel-package-maven-plugin/src/main/java/org/apache/camel/maven/packaging/MojoHelper.java
Add to the getComponentPath() switch:
case "camel-aws": return Arrays.asList(dir.resolve("camel-aws2-athena"), // ... dir.resolve("camel-your-new-component"));
Parent folders needing registration:
camel-ai (langchain4j, vector dbs)camel-awscamel-azurecamel-googlecamel-ibmcamel-huaweicamel-debeziumcamel-vertxcamel-microprofilecamel-infinispancamel-cxfcamel-spring-parentcamel-testWithout this, the build won't pick up your component for catalog/docs generation.
mirror.gcr.io or Red Hat quay.io ones.amd64, ppc64le and s390x). Sometimes the image is provided in different registries such as icr.io. If not available, use skipITs.ppc64le and skipITs.s390x Maven properties to disable it.CAMEL-XXXX: Brief description
Reference JIRA when applicable.
gh api -X GET /user/starredgh api -X PUT /user/starred/apache/camel