Backend Kotlin
Kotlin/JVM services, APIs and workers (Ktor, Spring, coroutines, JDBC/Exposed) with explicit contracts, owned coroutine lifetimes, preserved failures and cancellation, safe inputs and evidenced completion. Rule IDs KT, AR, ER, CO, BE, QA; NEED TO blocks on proven violations.
Set this profile
ccpp cpo profile set backend-kotlinInside Claude Code, prefix the command with ! to run it in the session. Downloading a profile needs a Builder or Team plan; listing them does not.
Policies
78 policies, grouped by what they guard.
Secrets & private data 4
Credentials, keys and personal data stay out of files, logs, commands and the conversation.
kt-be15-config-credentialsBE-15 · Ktor and Spring configuration files reference secrets through environment placeholders, not literal values
kt-be15-credential-literalsBE-15 · Credential-named values are not initialized with string literals in production code (review)
kt-be15-credential-sinksBE-15 · Credential parameters and properties (password, secret, API key, signing key) never receive a literal in production code
kt-be16-sensitive-loggingBE-16 · Logs never receive passwords, tokens, keys, Authorization or Cookie headers
Authentication & authorization 3
Who may do what, from roles, privileges and permissions to CSRF, CORS and verified tokens.
kt-be03-unprotected-endpointsBE-03 · Routes and controller endpoints in declared protected paths require authentication (off until protectedEndpointPaths is set)
kt-be05-authentication-removedBE-05 · The user approves removing authenticate { }, @PreAuthorize/@Secured/@RolesAllowed or making authentication optional
kt-be05-security-weakeningBE-05 · The user approves broad permit-all rules, disabled CSRF and credentialed wildcard CORS
Untrusted input 4
Requests, downloads and tool output are validated before they reach SQL, a shell, eval or a deserializer.
kt-be01-unvalidated-request-bodiesBE-01 · Request bodies bound by Spring or Ktor are validated before use (@Valid, RequestValidation, require/validate) (review)
kt-be02-entity-request-bindingBE-02 · Request bodies bind dedicated input models, not persistence entities (definition from kotlin-lsp) (review)
kt-be06-sql-from-requestBE-06 · Request parameters are never interpolated or concatenated into SQL text (Ktor call parameters, Spring request parameters)
kt-be06-sql-textBE-06 · The user confirms SQL text built by interpolating or concatenating non-constant values
Network & TLS 1
Outbound data, verified certificates, destinations taken from requests and services exposed to others.
kt-be18-request-controlled-destinationsBE-18 · Outbound HTTP destinations are not taken from request parameters without a destination check (SSRF)
Weakened checks 2
Suppressions, disabled lint or CI steps and relaxed strictness need a reason or the user's approval.
kt-qa06-weakened-checksQA-06/QA-08 · The user approves build or CI changes that skip tests, lint or warnings-as-errors, ignore failures or widen opt-ins
kt-qa07-broad-suppressionsQA-07 · No new file-level @file:Suppress/@file:OptIn or ktlint-disable; declaration-level suppressions name their reason
Type safety 4
No escapes from the type checker (any, unchecked casts, non-null assertions) and explicit contracts.
kt-kt03-not-null-assertionsKT-03 · New '!!' assertions are replaced by a validated contract, an explicit failure or safe nullable handling (review)
kt-kt04-unchecked-castsKT-04 · No new unchecked cast reported by the Kotlin compiler (UNCHECKED_CAST, kotlin-lsp)
kt-kt05-platform-typesKT-05 · Declarations do not expose a type inferred from a Java platform call (HasPlatformType, kotlin-lsp)
kt-kt06-untyped-contractsKT-06 · Stable contracts (DTOs, API and event models) use named types instead of Any, Pair/Triple or string-keyed Any maps
Error handling 7
Failures are propagated or handled, never swallowed or turned into fake success.
kt-be09-retry-side-effectsBE-09 · A retry around a side effect (write, send, charge, publish) shows how a replay stays safe (review)
kt-er02-default-after-failureER-02 · A broad catch does not turn an unexpected failure into null, zero or empty data without a declared contract (review)
kt-er02-swallowed-failuresER-02 · A catch never swallows a failure silently nor reports success after it
kt-er03-lost-causeER-03 · A translated exception keeps the original as its cause
kt-er04-return-in-finallyER-04 · No return inside finally overrides the pending result or exception
kt-er05-broad-catchER-05 · Application logic catches specific recoverable failures, not Exception or Throwable (outside error boundaries)
kt-er06-generic-exceptionsER-06 · Failures are thrown as specific types, not bare Exception, RuntimeException or Throwable
Async & concurrency 16
Owned tasks, awaited promises, preserved cancellation and no blocking on event loops.
kt-co01-unowned-scopesCO-01 · Coroutines are not launched in GlobalScope or in a detached CoroutineScope(...) nobody can cancel (resolved by kotlin-lsp)
kt-co02-replaced-parent-jobCO-02 · Coroutines started from request-owned code keep their parent instead of a new Job, SupervisorJob or NonCancellable (review)
kt-co03-unreleased-executorsCO-03 · Scopes, dispatchers and executors a component creates are cancelled or shut down by it; shared dispatchers are never closed
kt-co04-caught-cancellationCO-04 · An explicit catch of CancellationException rethrows it
kt-co04-swallowed-cancellationCO-04 · A catch around suspending calls does not swallow CancellationException (SuppressedCancellationException, kotlin-lsp)
kt-co04-translated-cancellationCO-04 · A broad catch around suspending calls does not translate CancellationException into an application exception (suspend calls resolved by kotlin-lsp)
kt-co05-runcatching-cancellationCO-05 · Standard runCatching does not wrap suspending work whose cancellation it then consumes (SuppressedCancellationException, kotlin-lsp)
kt-co06-run-blocking-in-coroutinesCO-06 · runBlocking is not called from suspend functions or coroutine bodies (RunBlockingInSuspendFunction, kotlin-lsp)
kt-co07-sleep-in-coroutinesCO-07 · Coroutine code waits with delay(), not Thread.sleep()
kt-co08-blocking-io-on-event-loopCO-08 · Blocking JDBC, file and HTTP calls (resolved by kotlin-lsp) do not run on Dispatchers.Default or a declared event loop
kt-co09-uncooperative-loopsCO-09 · Long-running loops inside coroutines check for cancellation or suspend (review)
kt-co11-unobserved-supervisionCO-11 · Supervised children have an observed failure path (handler, try/catch or result)
kt-co12-hardcoded-dispatchersCO-12 · Business logic receives its dispatcher instead of hard-coding Dispatchers.IO/Default (tests control scheduling)
kt-co13-volatile-compound-updatesCO-13 · A @Volatile property is not updated with a non-atomic read-modify-write (++, +=, x = x + …)
kt-co14-suspend-flow-factoriesCO-14 · Functions returning a cold Flow are not suspend; setup runs inside the flow builder
Performance & resource bounds 6
Timeouts, bounded retries and fan-out, reused clients, closed resources, no N+1 queries.
kt-be10-unbounded-retriesBE-10 · Retries are bounded (attempts or deadline); no endless Flow.retry, retryWhen { true } or catch-and-loop
kt-be11-outbound-timeoutsBE-11 · HTTP clients and requests are built with finite timeouts
kt-be12-unbounded-page-sizesBE-12 · Caller-controlled limit, size and page parameters are capped before use (review)
kt-be13-clients-per-callBE-13 · HTTP clients, connection pools and executors are created once and shared, not per call
kt-be14-unclosed-resourcesBE-14 · Owned connections, statements, streams and responses are closed on every path (use, finally) or handed to an owner
kt-co10-unbounded-fanoutCO-10 · Fan-out over an input collection (launch/async per item) has a concurrency bound
Architecture & boundaries 8
Declared layers and dependency directions, no import cycles, small modules, explicit dependencies.
kt-ar01-gradle-boundariesAR-01 · Gradle dependencies respect the declared module boundaries (off until gradleDependencyRules is set)
kt-ar01-import-boundariesAR-01 · Imports respect the project's declared code boundaries (off until importRules is set)
kt-ar02-gradle-project-cyclesAR-02 · A new Gradle project dependency does not close a cycle in the module graph (followed three hops deep)
kt-ar04-library-api-typesAR-04 · Public declarations of declared library APIs state their return and property types (off until libraryApiPaths is set)
kt-ar05-library-api-docsAR-05 · Public classes and functions of declared library APIs carry KDoc (off until libraryApiPaths is set)
kt-ar06-service-locatorAR-06 · Business code receives collaborators explicitly instead of resolving them from a service locator
kt-ar07-pure-domainAR-07 · Declared pure domain code imports no transport, persistence or I/O packages (off until domainPaths is set)
kt-kt09-deferred-dependenciesKT-09 · Collaborators arrive through the constructor, not as lateinit fields set after construction
Language idioms 5
The conventions of the language that keep code predictable for its readers.
kt-kt01-unreassigned-varKT-01 · A local var that is never reassigned is declared val
kt-kt02-exposed-mutable-stateKT-02 · Classes expose read-only collections and state flows; mutable ones stay private to their owner
kt-kt07-ambiguous-boolean-argumentsKT-07 · Calls passing several Boolean literals positionally name them
kt-kt08-catch-all-closed-whenKT-08 · A when over a sealed or enum type handles its variants without an else that hides new cases (definition from kotlin-lsp)
kt-kt10-shadowed-implicit-itKT-10 · Nested lambdas name their parameters instead of shadowing the outer implicit 'it' (kotlin-lsp)
HTTP & APIs 3
Request and response contracts, schemas, status codes, error bodies and published API surfaces.
kt-be17-exception-details-exposedBE-17 · Error responses carry no stack traces, and no messages of unexpected exceptions (StatusPages, catch-all handlers)
kt-qa10-api-dump-commandsQA-10 · The user confirms regenerating API dumps (apiDump, updateLegacyAbi) instead of reviewing the API change
kt-qa10-api-snapshotsQA-10 · The user approves changes that remove or alter signatures in published API dumps (binary-compatibility-validator .api files)
Databases & queries 2
Schemas, queries, transactions and the ORM used as intended.
kt-be07-multi-write-transactionBE-07 · A function with several database writes runs them in one transaction or states its consistency protocol (review)
kt-be08-transaction-thread-handoffBE-08 · Work inside a thread-bound transaction does not hop to another thread or coroutine (review)
Logging & observability 1
Structured logs, bounded metric labels, no debug prints left in production code.
kt-be19-console-outputBE-19 · Production failures go through the logging system, not println or printStackTrace
Dependencies & toolchain 3
Packages change through the package manager, lockfiles stay consistent and the project's tools are used.
kt-ar08-new-dependenciesAR-08 · A new Gradle dependency or version-catalog library names the changed code that uses it (review)
kt-qa11-build-wrapper-hintQA-11 · Reminds Claude to use the checked-in wrapper when gradle or mvn runs in a form the rewrite leaves alone (paths, chains, wrappers)
kt-qa11-build-wrapperQA-11 · Builds run through the checked-in Gradle or Maven wrapper (gradle → ./gradlew, mvn → ./mvnw)
Tests 4
Tests assert behavior, stay deterministic and are not removed or skipped silently.
kt-qa01-untested-changesQA-01/QA-02 · A turn that changes production Kotlin code changes tests too, or says why none applies (review, once per stop)
kt-qa04-coroutine-testsQA-04 · Coroutine unit tests use runTest and virtual time, not runBlocking or Thread.sleep
kt-qa06-disabled-testsQA-06 · The user approves newly disabled, ignored or skipped tests
kt-qa06-removed-testsQA-06 · The user approves edits that remove tests or assertions from a Kotlin test file
Completion evidence 5
A turn ends with its checks run and passing; claims of success need proof after the last edit.
kt-er01-placeholdersER-01 · Production code does not ship kotlin.TODO() or a thrown NotImplementedError (resolved by kotlin-lsp)
kt-qa09-completion-claimsQA-09 · A final message claiming a passing build, tests or lint needs a successful Gradle or Maven run after Claude's last edit
kt-qa09-evidence-ledgerQA-09 · Records, per session, Claude's edits and the Gradle or Maven checks that succeeded after them
kt-qa09-new-compile-errorsQA-09 · Claude does not end a turn with compiler errors it introduced in Kotlin sources or the files that use them (kotlin-lsp)
kt-turn-baselineRecords the work tree when a prompt starts, the baseline of the end-of-turn checks (QA-01, QA-09)