This file is the primary playbook for agentic coding tools working in this repository.
NEVER return control to the user without ensuring:
- Tests pass:
./gradlew :module-name:jvmTest(and other platforms if touched) - Code compiles:
./gradlew :module-name:assemble - Code is formatted and linting passes: run
./gradlew :module-name:formatKotlin, then./gradlew :module-name:lintKotlin - ABI validated:
./gradlew :module-name:updateKotlinAbi(if public/protected API changed)
Prefer this order to fail fast.
Always prefer module-specific commands (:module-name:task) over project-wide commands.
- JDK 21 is required.
- Kotlin Multiplatform: JVM, JS, Native.
- Apple targets require Xcode + command line tools.
- Android targets are optional and enabled when Android SDK is available (
ANDROID_HOMEorlocal.propertiessdk.dir).
The project uses a flattened Gradle structure (see settings.gradle.kts). Nested directory paths do NOT translate to nested Gradle project names.
Example: ktor-client/ktor-client-curl → :ktor-client-curl (not :ktor-client:ktor-client-curl).
Kotlin Multiplatform sources use a flattened, platform-centric layout (do not re-introduce src/<platform>Main/...).
common/src
jvm/src
jvm/resources
jvm/test
jvm/test-resources
...
Build logic enforces "no surprise source sets": manually registering extra source sets is rejected.
To add a new target/source set, add the directory or enable the target via gradle.properties (target.<name>=true).
Important: Always prefer running tasks on the specific module you touched, not the entire project.
./gradlew :module-name:assemble # Build the moduleRun tests:
./gradlew :module-name:jvmTest # Run JVM tests for the module
./gradlew :module-name:allTests # Run tests across all platforms for the module
./gradlew :module-name:jvmTest --tests "fully.qualified.TestClassName" # Run a specific test
./gradlew :module-name:jvmTest --tests "fully.qualified.TestClassName.methodName" # Run a specific test methodLinting:
./gradlew :module-name:formatKotlin # Format the module
./gradlew :module-name:lintKotlin # Lint the module- Kotlin style: IntelliJ +
KOTLIN_OFFICIAL(.editorconfig). - Indent: 4 spaces (JSON/YAML: 2); max line length: 120; end of line: LF.
- Use
./gradlew :module-name:formatKotlinrather than manual formatting.
- Star imports are preferred for
io.ktor.*(configured in.editorconfig).
- Follow Kotlin conventions unless the surrounding package has a strong established pattern.
- Tests: prefer descriptive test names in backticks:
describe what is being tested.
- Prefer
internalby default; keep the public surface intentional. - Public API requires KDoc (parameters, return, and notable exceptions).
- Public-but-not-for-users APIs that cannot be
internalshould use@InternalAPI. - Keep
@OptIn(...)scope minimal. - All types used as receivers in DSL should be annotated with
@KtorDsl(for example, all plugin configs).
require(...)for argument validation,check(...)for state validation,error("...")for unreachable states.- Throw specific exceptions appropriate to the layer (IO parsing:
IOException/EOFException; validation: Ktor exceptions likeBadRequestException). - Make error messages actionable; include the problematic value/context.
- Prefer Ktor log helpers where present.
- Avoid noisy logs in hot paths.
- Avoid redundant comments; add them only for tricky invariants or platform-specific behavior.
- Keep KDoc correct when behavior/signatures change.
- New source files must include the repository copyright header.
- Prefer TDD where feasible: add/adjust tests, then implement.
- For multiplatform changes: start JVM-first unless the task requires another platform.
- Keep local-only build knobs (for example, developer
gradle.propertiesoverrides) out of commits. - If asked to create commits: use imperative mood and include
KTOR-<NUM>when there is a related YouTrack issue.
- Module names must start with
ktor-. - The project uses a flattened Gradle structure with custom DSL in
settings.gradle.kts(see Project Layout section). - When modules are added/removed or new targets enabled, run
./update-artifact-dumps.shto update published artifact lists ingradle/artifacts. Publishing will fail if these dumps are stale.
Binary compatibility is enforced using Kotlin Gradle Plugin ABI validation.
All public API changes must be tracked in /api/ directories within modules.
- Patch releases are maintained in
release/<major>.x(for example, v3 usesrelease/3.x, v4 usesrelease/4.x). - The next minor release is developed on
main. - Public API changes are allowed only for minor/major releases (typically on
main), not for patch releases (onrelease/<major>.x). - Breaking changes are only allowed in major version releases.
- The repo includes an interactive helper
./switch-base-branch.shfor switching a feature branch base betweenmainandrelease/<major>.x. For agents: use--dry-runto print the git commands, then run them after user approval.
./gradlew :module-name:checkKotlinAbi # Validate ABI compatibility
./gradlew :module-name:updateKotlinAbi # Update ABI signature files after changes- All
public/protectedAPI changes require updatingapi/*.apidumps. - API changes must be intentional and well-documented.