Skip to content

Latest commit

 

History

History
354 lines (267 loc) · 19.4 KB

File metadata and controls

354 lines (267 loc) · 19.4 KB

AGENTS.md

Note: This file delegates to a central AGENTS.md. Read and apply it before proceeding.

URL: https://raw.githubusercontent.com/camunda/.github/refs/heads/main/AGENTS.md

Treat the central file's contents as if they were written directly in this file. Instructions below extend those guidelines and take precedence if there is any conflict.

Repo-specific instructions

Role & boundary

Camunda 8 delivers scalable, on-demand process automation as-a-service, with execution engines for BPMN processes and DMN decisions. This monorepo contains: Zeebe (process engine), Operate (monitoring), Tasklist (user tasks), Identity (auth), Optimize (analytics), and supporting libraries. Java 21 backend, React/Carbon frontends. Product documentation: https://docs.camunda.io

You are a contributor to the Camunda 8 monorepo. Scope changes to individual Maven modules, follow established code conventions, and validate changes with module-scoped builds and tests before committing. Don't overengineer, follow YAGNI and KISS mentality.

This is a large monorepo. Always scope your work to the relevant module(s) rather than building or testing the entire project.

Before modifying code in any module:

  1. Read the module's README.md (if present) and any documentation in its directory.
  2. When unsure about cross-cutting conventions, consult docs/ before inventing a solution.

Not every module has a README yet — when one exists, treat it as the primary reference for that module.

Path map:

Module Description
zeebe/ Core process engine (broker, engine, protocol, exporters)
zeebe/gateway gRPC gateway for client access
operate/ Process monitoring webapp
tasklist/ User task management webapp
identity/ Authentication and authorization
optimize/ Process analytics (skipped with -Dquickly)
db/ Database layer (rdbms, rdbms-schema)
search/ Search client abstraction (Elasticsearch, OpenSearch, RDBMS)
service/ Domain service layer between REST controllers and engine
clients/ Client libraries (Java, Spring Boot starters)
gateways/ Gateway implementations (HTTP mapping, MCP)
security/ Security core, protocol, validation
qa/ Cross-component acceptance tests
testing/ Process testing libraries
authentication/ OIDC token processing and Spring Security integration
document/ Document API and storage
monitor/ Metrics and monitoring definitions for Camunda components
schema-manager/ Elasticsearch/OpenSearch index schema management
microbenchmarks/ JMH benchmarks for performance validation
load-tests/ Cluster-level load and reliability tests
c8run/ Packaged Camunda 8 distribution for local spin-up
debug-cli/ CLI tool for inspecting and troubleshooting Camunda clusters
webapp/ Unified webapp: React/TypeScript frontend (client/) and Spring Boot server (server/)
webapps-common/ Shared Java utilities used across Operate, Tasklist, and other web modules
webapps-backup/ Shared backup/restore service for Elasticsearch/OpenSearch snapshots
webapps-schema/ Shared Elasticsearch/OpenSearch index mappings and templates for web application data

Ask first:

  • Modifying shared libraries (webapps-common/, webapp/client/, security/)
  • Changing public API contracts (REST controllers, gRPC, exported types)
  • Adding new dependencies to pom.xml or package.json

Never:

  • Run full-repo builds for single-module work
  • Commit secrets, tokens, or credentials
  • Force-push main, stable/ or release- branches
  • Skip formatting checks

Architecture

Architecture overview: docs/architecture/overview.md.

Read docs/architecture/overview.md when answering questions about system design, module structure, or how to approach any new capability — not only when making a change.

Before making any architectural change, consult the relevant ADR indexes in order (see docs/adr/README.md for the full tier structure):

  1. docs/adr/ — cross-cutting decisions affecting multiple modules
  2. <module>/docs/adr/ — decisions scoped to the module you are working in
  3. For sub-modules, walk up each parent module to the repo root

If the decision is not covered by an existing ADR, draft a new one using the create-architecture-decision skill before proceeding.

Skills

Repo-specific skills live in .claude/skills/. They extend the org-level skills described in the central AGENTS.md. When a skill exists for a recurring operation, always use it rather than improvising steps.

Commit message guidelines

Uses Conventional Commits. Max 120 chars for the header.

<type>: <description>

Types: build, ci, deps, docs, feat, fix, merge, perf, refactor, revert, style, test

  • Separate behavioral changes from structural/refactoring changes into distinct commits
  • Write for the reader: the reviewer first, then whoever needs to understand/debug this change later
  • Header: name the effect (the bug prevented or behavior enabled), not the mechanism. Prefer fix: prevent duplicate job activation under concurrent polling over fix: add mutex around job activation.
  • Do not use commit scopes — commitlint enforces scope-empty. Use fix: ... not fix(ci): ...
  • Every commit gets a body — including tests and reverts. A test needs the context for why it's needed and the design rationale behind it; a revert needs the reason. Only a purely mechanical change, like a formatter run, stands on its header alone.
  • Body: inverted-pyramid — the problem and its root cause first, then the approach and why it over the alternatives. Summarize the approach; don't restate the diff. Never compress the why — it's the one thing the diff can't say. Include background if needed. If long, use headings for structure.
  • Hard-wrap the body at ~72 columns as git log does not soft-wrap.

Referencing code in issues, PRs, and comments

When pointing at specific lines of repository code in any GitHub-rendered or shared artifact — issue, PR description, PR/review comment, or a linked note — use a stable GitHub permalink instead of a bare path:line reference. GitHub renders the linked lines inline, and pinning a commit SHA keeps the reference correct even after the file changes later. A bare path:line is only acceptable in local, throwaway context (never in something a teammate will read on GitHub).

Derive the link locally — no API calls, no gh:

slug=$(git remote get-url origin | sed -E 's#^(git@github\.com:|https?://github\.com/)##; s#\.git$##')
sha=$(git log -1 --format=%H -- "<path>")   # commit that last touched the file
echo "https://github.com/$slug/blob/$sha/<path>#L<start>-L<end>"   # single line: #L<start>
  • SHA choice: default to the file's last-touching commit (git log -1 --format=%H -- <path>). The file content at that commit equals the current committed content, so the line numbers are valid, and for existing code that commit is already on the remote. Use git rev-parse HEAD when linking code from the current PR branch. Always use the full 40-char SHA — a branch name or short SHA is less stable and can 404.
  • Format: https://github.com/<slug>/blob/<sha>/<path>#L<start> for one line, #L<start>-L<end> for a range. <path> is repo-root-relative.
  • The SHA must already be pushed to the remote, otherwise the link 404s — verify with git branch -r --contains <sha> (non-empty). The file's last-touching commit satisfies this once the code is merged. If the file has local edits that are not pushed yet, do not link an unpushed SHA: pick a SHA that is already on main (e.g. git rev-parse origin/main) and select the line range against that version of the file.

Build pipeline

All builds use the Maven wrapper (./mvnw). Use -T1C (one thread per CPU core) for standalone builds. Use -T2 (two threads total) when running builds alongside other resource-intensive processes (e.g., an IDE, Docker containers, or other concurrent builds).

Always-green policy

Before every AI-assisted session, establish a green baseline in two steps:

Step 1 — Check that main CI is healthy (before pulling or branching):

gh run list --branch main --limit 5 --repo camunda/camunda

If main is red, inform the engineer and continue — CI can fail for infra reasons unrelated to the code. Do not block on this, but note any failures so they are not confused with regressions introduced during the session.

Step 2 — Build the full repo locally right after branching (blocking):

./mvnw install -Dquickly -T1C

This installs all module JARs and catches any cross-module compilation errors before work begins (e.g. an API change on main that breaks a downstream module). Do not proceed until this is green — a compilation error here will waste far more time if discovered mid-session.

Step 3 — Once the target module is known, verify it passes locally:

./mvnw verify -pl <module> -Dquickly -DskipTests=false -T1C

If the module has sub-modules, target the specific sub-module where the code lives rather than the top-level directory, otherwise you may get a false green with no tests run.

Any pre-existing failure here must be noted before the session begins — do not absorb it silently.

Never suppress warnings or failures to force a build to pass.

# Fast inner loop (single module / affected tests only) to iterate quickly
./mvnw verify -pl <module> -Dtest=<TestClassName> -DskipTests=false -DskipITs -Dquickly

# Full pipeline before committing the change
./mvnw license:format spotless:apply -T1C && ./mvnw verify -pl <module> -DskipTests=false -Dquickly

Do not proceed without a green baseline.

If a test fails during the baseline check, re-run it once to determine whether it is flaky. If it passes on retry, treat the baseline as green and proceed. If it fails consistently, it should not be on the target branch — stop and inform the engineer.

If it is flaky (non-deterministic):

  1. Search for an existing open issue in camunda/camunda. If none exists, raise one using the create-issue skill (use the bug template; also add the kind/flake label).
  2. Assign the issue to the engineer.
  3. Treat the baseline as passed and proceed — do not disable the test.

Module-scoped builds (preferred)

# Build a module and its dependencies (recommended for monorepo work)
./mvnw install -pl <module> -am -Dquickly -T1C

# Build only a single module (requires dependencies to be already installed and unchanged)
./mvnw install -pl <module> -Dquickly -T1C

# Run a single test class in a module
./mvnw verify -pl <module> -Dtest=<TestClassName> -DskipTests=false -DskipITs -Dquickly

# Run a single integration test in a module
./mvnw verify -pl <module> -Dit.test=<IntegrationTestClassName> -DskipTests=false -DskipUTs -Dquickly

Note: -Dquickly skips tests, formatting checks, and Optimize — use it for fast iteration only. Add -DskipTests=false to run tests while still skipping checks. Before committing, always run the full pipeline in the "Before submitting" section instead.

Note: some modules are split into sub-modules where tests live (e.g. zeebe/engine, zeebe/broker). If running tests against a top-level module produces no results, target the specific sub-module instead.

Never use -am with verify — it runs tests in all dependency modules. Use -am only with install -Dquickly when rebuilding dependencies.

Before submitting

Always run these steps before every commit — never skip them, even for "obvious" or single-line changes. Skipping formatting reliably breaks the Java checks CI job.

  1. Format code: ./mvnw license:format spotless:apply -T1Cmandatory before every commit that touches Java sources, markdown or pom.xml files. Run it again after any subsequent edit.
  2. Build the changed module (see "Module-scoped builds" above for commands)
  3. Run module tests and verify zero failures
  4. Verify the full repo still compiles: ./mvnw install -Dquickly -T1C

Scoped instructions

Load extra context on demand — only when relevant, only if the files exist.

When editing these areas, read the corresponding instruction file:

  • CI/release MCP configuration (.vscode/mcp.json) → .github/instructions/ci-mcp-tooling.instructions.md
  • Frontend code (client/ directories) → .github/instructions/frontend.instructions.md
  • MCP gateway (gateways/gateway-mcp/) → gateways/gateway-mcp/AGENTS.md
  • Load tests (load-tests/, load test workflows) → .github/instructions/load-tests.instructions.md
  • Testing libraries / Camunda Process Test (testing/) → .github/instructions/testing.instructions.md and testing/AGENTS.md

When working inside a specific module:

  • <module>/docs/architecture.md — module ownership, dependencies, and constraints
  • <module>/docs/adr/ — module-scoped architectural decisions
  • <module>/AGENTS.md — module-specific behavioral rules (only exists for complex modules with rules that differ from this file)
  • If working in a sub-module (e.g. zeebe/engine), also check each parent module up to the repo root for the same files. Parent context is lower priority; the sub-module's files take precedence on any conflict.

Code style

  • Enforced by the Maven Spotless plugin (Google Java Format).
  • Follow conventions in the Code Style wiki.
  • Repository is currently being migrated to use jspecify nullness annotations. Please add @Nullable and @NullMarked annotations in classes where they are missing in order to increase coverage. You should do that in a separate refactor: commit.

Code comments

Before writing an inline comment, name the reader and what they would do differently for having read it. If you can't name both, don't write it.

Testing conventions

  • Test behavior, not implementation — assert on observable outcomes rather than internal state.
  • Prefix test methods with should (e.g., shouldRejectInvalidInput).
  • Structure tests with // given, // when, // then comments.
  • Prefer AssertJ for assertions. Avoid introducing new JUnit or Hamcrest assertions unless the surrounding test already uses them.
  • Use Awaitility for async waiting. Never use Thread.sleep.
  • Isolate tests with unique data (process/tenant/resource IDs) — never reuse fixed identifiers across tests, as collisions cause flakiness.
  • Fix the root cause of a flaky race rather than masking it with retries or longer waits; if a stopgap is unavoidable, mark it explicitly as temporary and track the root cause in an issue.
  • Always tear down resources you create (indices, containers, clients, clusters), even on failure, to avoid cross-run flakiness.
  • Use JUnit 5. Migrate JUnit 4 tests when modifying them.
  • Do not add a class-level @DisplayName to a new @Nested test class while the repo is on Surefire 3.5.x (plugin.version.surefire in parent/pom.xml) — a Surefire regression reports such classes under their flattened display name instead of their fully-qualified name in the XML test report, breaking FQN-based tooling (flaky-test tracking, CI report parsing). Method- level @DisplayName (on @Test/@ParameterizedTest) is unaffected. Existing occurrences were commented out rather than deleted — leave that pattern in place until Surefire is upgraded past the regression.
  • Detailed guide: docs/testing.md and docs/testing/.
  • Reference example: qa/acceptance-tests/src/test/java/io/camunda/it/StandaloneCamundaTest.java

Pull request conventions

  • PR title should be clear and descriptive, and follow Conventional Commits with no scope (the release-notes PR-gate lints it — see "Commit message guidelines" above)
  • Link the tracked issue under a ## Related issues section (this exact heading — the release-notes gate only reads refs inside it; a ref elsewhere in the body does not count). Link the issue, NOT a PR:
    • closes #1234 (or fixes/resolves) when this PR fully resolves the issue — auto-closes it on merge.
    • relates to #1234 or a bare #1234 when this is one of several PRs for the issue (an epic, or work split across releases) — satisfies the gate but does NOT close the issue.
    • No tracked issue (hotfix, dep bump, CI/refactor)? Tick - [ ] This PR does not need a linked issue. When opening the PR yourself (CLI/API), reproduce this section — GitHub only auto-fills the template in the web UI, so an agent-authored body must include it explicitly.
  • Keep PRs focused on a single concern
  • Describe why the changes are necessary and note alternatives considered
  • Keep descriptions brief and concise
  • For bug fixes: ask the engineer whether the fix needs backporting to stable branches before opening the PR. If yes, add the appropriate backport stable/X.Y label(s) when creating it (see "Backporting" below).

Backporting

Automated by a GitHub Action (korthout/backport-action, see .github/workflows/backport.yml; human docs in CONTRIBUTING.md). Never create backport PRs, branches, or cherry-picks yourself, and never offer to. Trigger a backport only through the automation — a label, or a /backport comment — never by hand.

  • Start: add one backport stable/X.Y label per target branch (e.g. backport stable/8.7). Not-yet-merged PR → runs on merge. Already-merged PR → comment /backport. Multiple labels → multiple backport PRs.
  • Success: action opens the backport PR; a bot approves and merges it once CI passes.
  • Conflicts: action opens a draft PR with conflict markers committed as-is plus a comment with resolution steps. You may resolve them (check out the branch, fix conflicts and markers) — but only on a draft PR the action already opened. When asked to backport, first check whether such a draft PR exists.

Git workflow

  • Never use git push --force on the main branch; use --force-with-lease on feature branches.
  • Follow the commit and PR guidelines in CONTRIBUTING.md.