ccppccpp home

Profiles

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-kotlin

Inside 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-credentials

    BE-15 · Ktor and Spring configuration files reference secrets through environment placeholders, not literal values

  • kt-be15-credential-literals

    BE-15 · Credential-named values are not initialized with string literals in production code (review)

  • kt-be15-credential-sinks

    BE-15 · Credential parameters and properties (password, secret, API key, signing key) never receive a literal in production code

  • kt-be16-sensitive-logging

    BE-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-endpoints

    BE-03 · Routes and controller endpoints in declared protected paths require authentication (off until protectedEndpointPaths is set)

  • kt-be05-authentication-removed

    BE-05 · The user approves removing authenticate { }, @PreAuthorize/@Secured/@RolesAllowed or making authentication optional

  • kt-be05-security-weakening

    BE-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-bodies

    BE-01 · Request bodies bound by Spring or Ktor are validated before use (@Valid, RequestValidation, require/validate) (review)

  • kt-be02-entity-request-binding

    BE-02 · Request bodies bind dedicated input models, not persistence entities (definition from kotlin-lsp) (review)

  • kt-be06-sql-from-request

    BE-06 · Request parameters are never interpolated or concatenated into SQL text (Ktor call parameters, Spring request parameters)

  • kt-be06-sql-text

    BE-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-destinations

    BE-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-checks

    QA-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-suppressions

    QA-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-assertions

    KT-03 · New '!!' assertions are replaced by a validated contract, an explicit failure or safe nullable handling (review)

  • kt-kt04-unchecked-casts

    KT-04 · No new unchecked cast reported by the Kotlin compiler (UNCHECKED_CAST, kotlin-lsp)

  • kt-kt05-platform-types

    KT-05 · Declarations do not expose a type inferred from a Java platform call (HasPlatformType, kotlin-lsp)

  • kt-kt06-untyped-contracts

    KT-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-effects

    BE-09 · A retry around a side effect (write, send, charge, publish) shows how a replay stays safe (review)

  • kt-er02-default-after-failure

    ER-02 · A broad catch does not turn an unexpected failure into null, zero or empty data without a declared contract (review)

  • kt-er02-swallowed-failures

    ER-02 · A catch never swallows a failure silently nor reports success after it

  • kt-er03-lost-cause

    ER-03 · A translated exception keeps the original as its cause

  • kt-er04-return-in-finally

    ER-04 · No return inside finally overrides the pending result or exception

  • kt-er05-broad-catch

    ER-05 · Application logic catches specific recoverable failures, not Exception or Throwable (outside error boundaries)

  • kt-er06-generic-exceptions

    ER-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-scopes

    CO-01 · Coroutines are not launched in GlobalScope or in a detached CoroutineScope(...) nobody can cancel (resolved by kotlin-lsp)

  • kt-co02-replaced-parent-job

    CO-02 · Coroutines started from request-owned code keep their parent instead of a new Job, SupervisorJob or NonCancellable (review)

  • kt-co03-unreleased-executors

    CO-03 · Scopes, dispatchers and executors a component creates are cancelled or shut down by it; shared dispatchers are never closed

  • kt-co04-caught-cancellation

    CO-04 · An explicit catch of CancellationException rethrows it

  • kt-co04-swallowed-cancellation

    CO-04 · A catch around suspending calls does not swallow CancellationException (SuppressedCancellationException, kotlin-lsp)

  • kt-co04-translated-cancellation

    CO-04 · A broad catch around suspending calls does not translate CancellationException into an application exception (suspend calls resolved by kotlin-lsp)

  • kt-co05-runcatching-cancellation

    CO-05 · Standard runCatching does not wrap suspending work whose cancellation it then consumes (SuppressedCancellationException, kotlin-lsp)

  • kt-co06-run-blocking-in-coroutines

    CO-06 · runBlocking is not called from suspend functions or coroutine bodies (RunBlockingInSuspendFunction, kotlin-lsp)

  • kt-co07-sleep-in-coroutines

    CO-07 · Coroutine code waits with delay(), not Thread.sleep()

  • kt-co08-blocking-io-on-event-loop

    CO-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-loops

    CO-09 · Long-running loops inside coroutines check for cancellation or suspend (review)

  • kt-co11-unobserved-supervision

    CO-11 · Supervised children have an observed failure path (handler, try/catch or result)

  • kt-co12-hardcoded-dispatchers

    CO-12 · Business logic receives its dispatcher instead of hard-coding Dispatchers.IO/Default (tests control scheduling)

  • kt-co13-shared-mutation-in-coroutines

    CO-13 · State mutated from launch/async bodies is synchronized, atomic or confined (review)

  • kt-co13-volatile-compound-updates

    CO-13 · A @Volatile property is not updated with a non-atomic read-modify-write (++, +=, x = x + …)

  • kt-co14-suspend-flow-factories

    CO-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-retries

    BE-10 · Retries are bounded (attempts or deadline); no endless Flow.retry, retryWhen { true } or catch-and-loop

  • kt-be11-outbound-timeouts

    BE-11 · HTTP clients and requests are built with finite timeouts

  • kt-be12-unbounded-page-sizes

    BE-12 · Caller-controlled limit, size and page parameters are capped before use (review)

  • kt-be13-clients-per-call

    BE-13 · HTTP clients, connection pools and executors are created once and shared, not per call

  • kt-be14-unclosed-resources

    BE-14 · Owned connections, statements, streams and responses are closed on every path (use, finally) or handed to an owner

  • kt-co10-unbounded-fanout

    CO-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-boundaries

    AR-01 · Gradle dependencies respect the declared module boundaries (off until gradleDependencyRules is set)

  • kt-ar01-import-boundaries

    AR-01 · Imports respect the project's declared code boundaries (off until importRules is set)

  • kt-ar02-gradle-project-cycles

    AR-02 · A new Gradle project dependency does not close a cycle in the module graph (followed three hops deep)

  • kt-ar04-library-api-types

    AR-04 · Public declarations of declared library APIs state their return and property types (off until libraryApiPaths is set)

  • kt-ar05-library-api-docs

    AR-05 · Public classes and functions of declared library APIs carry KDoc (off until libraryApiPaths is set)

  • kt-ar06-service-locator

    AR-06 · Business code receives collaborators explicitly instead of resolving them from a service locator

  • kt-ar07-pure-domain

    AR-07 · Declared pure domain code imports no transport, persistence or I/O packages (off until domainPaths is set)

  • kt-kt09-deferred-dependencies

    KT-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-var

    KT-01 · A local var that is never reassigned is declared val

  • kt-kt02-exposed-mutable-state

    KT-02 · Classes expose read-only collections and state flows; mutable ones stay private to their owner

  • kt-kt07-ambiguous-boolean-arguments

    KT-07 · Calls passing several Boolean literals positionally name them

  • kt-kt08-catch-all-closed-when

    KT-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-it

    KT-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-exposed

    BE-17 · Error responses carry no stack traces, and no messages of unexpected exceptions (StatusPages, catch-all handlers)

  • kt-qa10-api-dump-commands

    QA-10 · The user confirms regenerating API dumps (apiDump, updateLegacyAbi) instead of reviewing the API change

  • kt-qa10-api-snapshots

    QA-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-transaction

    BE-07 · A function with several database writes runs them in one transaction or states its consistency protocol (review)

  • kt-be08-transaction-thread-handoff

    BE-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-output

    BE-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-dependencies

    AR-08 · A new Gradle dependency or version-catalog library names the changed code that uses it (review)

  • kt-qa11-build-wrapper-hint

    QA-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-wrapper

    QA-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-changes

    QA-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-tests

    QA-04 · Coroutine unit tests use runTest and virtual time, not runBlocking or Thread.sleep

  • kt-qa06-disabled-tests

    QA-06 · The user approves newly disabled, ignored or skipped tests

  • kt-qa06-removed-tests

    QA-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-placeholders

    ER-01 · Production code does not ship kotlin.TODO() or a thrown NotImplementedError (resolved by kotlin-lsp)

  • kt-qa09-completion-claims

    QA-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-ledger

    QA-09 · Records, per session, Claude's edits and the Gradle or Maven checks that succeeded after them

  • kt-qa09-new-compile-errors

    QA-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-baseline

    Records the work tree when a prompt starts, the baseline of the end-of-turn checks (QA-01, QA-09)