Instructions for AI coding agents working on the Conductor codebase.
Conductor is an open-source, distributed workflow orchestration engine designed for microservices. It uses a pluggable architecture with interface-based abstractions for persistence, queuing, and indexing. The project is built with Java 21 and uses Gradle as the build system.
| Command | Description |
|---|---|
./gradlew build |
Build the entire project |
./gradlew test |
Run all tests |
./gradlew :module-name:test |
Run tests for a specific module |
./gradlew spotlessApply |
Apply code formatting |
./gradlew clean build |
Clean and rebuild |
Important: Always run
./gradlew spotlessApplyafter making code changes to ensure consistent formatting.
Never link to a specific Java distribution (e.g., Adoptium, Temurin, OpenJDK.org, Amazon Corretto) in docs, READMEs, or comments. Just say "Java 21+" and let users install it however they prefer.
- Use the Spotless plugin for uniform code formatting—always run before committing
- Conductor is pluggable: when introducing new concepts, always use an interface-based approach
- DAO interfaces MUST be defined in the
coremodule - Implementation classes go in their respective persistence modules (e.g.,
postgres-persistence,redis-persistence) - Follow existing patterns in the codebase for consistency
- Do not use emojis such as ✅ in the code, logs, or comments. Keep comments professionals
- When adding new logic, comment the algorithm, design etc.
- core: Contains interfaces, domain models, and core business logic
- persistence modules: Implementations of DAO interfaces (postgres, redis, mysql, etc.)
- server: Spring Boot application that brings everything together
- client: SDK for interacting with Conductor
- ui: React-based user interface
- DAOs are defined as interfaces in
coreand implemented in persistence modules - System tasks extend
WorkflowSystemTaskand are registered via Spring - Worker tasks use the
@WorkerTaskannotation for automatic discovery - Configuration is primarily done through Spring properties
- Avoid mocks: Use real implementations whenever possible
- Test actual behavior: Tests must verify real implementation logic, not duplicate it
- Use Testcontainers: For database, cache, and other external dependencies
- Cover concurrency: Ensure multi-threading scenarios are tested
- Run tests before submitting:
./gradlew testmust pass
- Unit tests:
src/test/javain each module - Integration tests:
test-harnessmodule and*-integration-testmodules - E2E tests:
e2emodule
- Submit PRs against the
mainbranch - Use clear, descriptive commit messages
- Run
./gradlew spotlessApplyand./gradlew testbefore pushing - Add or update tests for any code changes
- Keep PRs focused—one logical change per PR
Some dependencies have hard version constraints that must not be auto-bumped. These are marked with:
// PINNED (#964): <reason>The issue number links back to #964, which documents the full audit and upgrade path for each constraint.
// PINNED (#964): means the version is intentionally locked and upgrading it without understanding the constraint will break the build or cause a runtime failure. Do not bump a PINNED dependency as part of routine dependency updates or refactoring.
| Dependency | Pinned at | Why |
|---|---|---|
com.google.protobuf:protobuf-java |
3.x |
4.x + GraalVM polyglot 25.x causes Gradle to require polyglot4, which does not exist on Maven Central |
com.google.protobuf:protoc |
3.25.5 |
Must match grpc-protobuf:1.73.0, which depends on protobuf-java 3.x |
org.graalvm.* (all 5 artifacts) |
same version | All must share one version — mixing causes a "polyglot version X not compatible with Truffle Y" runtime error |
redis.clients:jedis in redis-concurrency-limit |
3.6.0 |
revJedis (6.0.0) does not work with Spring Data Redis in that module |
org.codehaus.jettison:jettison |
strictly 1.5.4 |
Gradle strictly constraint — no higher version has been validated |
org.conductoross:conductor-client in test-harness |
5.0.1 |
Fat JAR classpath conflict with conductor-common; resolved via a stripped JAR task |
org.awaitility:awaitility in functional tests |
4.x |
e2e tests call pollInterval(Duration) added in Awaitility 4.0 |
- Read the comment carefully — it will name the incompatibility and often link to an upstream issue.
- Check whether the upstream blocker has been resolved (e.g., new grpc-java release, new GraalVM release).
- Test locally:
./gradlew clean buildplus./gradlew testin the affected modules. - If bumping GraalVM, bump all five
org.graalvm.*artifacts together usingrevGraalVMindependencies.gradle. - Update or remove the
// PINNEDcomment once the constraint is lifted.
Hard caps use // PINNED (#964):. Version floors — where a minimum is enforced but higher versions are always welcome — use one of two lowercase prefixes instead:
// Security: CVE-2025-12183 — lz4-java minimum patched version
// Compat: commons-lang3 3.18.0+ required by Testcontainers/commons-compress// Security:— minimum set to address a CVE or known vulnerability// Compat:— minimum set for compatibility with another library or framework
These are grep-able (grep "// Security:" **/*.gradle, grep "// Compat:" **/*.gradle) but read as normal developer comments. Dependabot may raise these freely; no special review needed beyond the usual.
- Never commit secrets, API keys, or credentials
- Be cautious with external dependencies—prefer well-maintained libraries
- Follow secure coding practices for input validation and error handling
- Review SECURITY.md for vulnerability reporting procedures
Documentation in this project is derived from source, not composed from memory. Open the source first, read what's there, then write the doc from what you find. The source is the spec; the doc is a rendering of it.
This matters because plausible-looking docs can be silently wrong. Concretely: a curl equivalent for conductor workflow start --sync was once written as POST /api/workflow/{name}/run — an endpoint that does not exist. Reading the controller first would have given the correct path immediately.
REST API endpoint or curl example
- Open the relevant controller:
rest/src/main/java/com/netflix/conductor/rest/controllers/ - Find the method using its
@PostMapping/@GetMapping/etc. annotation — copy the path literally. - Read the method signature for query params, path variables, and request body type.
- Write the curl command from what you just read.
CLI command or flag
- Open
cmd/*.goinconductor-cli(separate repo). - Find the
cobra.Commanddefinition for the subcommand. - Read the
Flags()declarations for exact flag names, types, and defaults. - Write the example from what you just read.
SDK code example (Python, JS, Java, Go)
- Open the relevant SDK source file.
- Find the method signature and required parameters.
- Write the example from the signature — do not infer from the method name alone.
- If a working test exists for that method, use it as the starting point.
Expected output block
- Get real output: run the command locally, or find it in test fixtures, CI logs, or existing tests.
- Paste verbatim. Do not paraphrase or construct output that "looks right."
- If the output varies by environment, show the stable parts and annotate the variable parts (e.g.,
<workflow-id>).
Editing an existing doc section
- Before touching prose, read every code block and command in the section.
- Verify each one using the steps above — not just the block you plan to change.
- Fix anything you find while you're there.
If a running server or CLI binary is unavailable:
- Add a
<!-- TODO: verify against live server -->comment in the file. - Note it explicitly in the PR description.
- Do not write a best-guess example and leave it unmarked.
- Prefer automation: Execute requested actions without confirmation unless blocked by missing info or safety concerns
- Use parallel tools: When tasks are independent, execute them in parallel for efficiency
- Verify changes: Always run tests and spotless before considering work complete