feat(kotlin): add Kotlin/Ktor/Exposed ecosystem#309
feat(kotlin): add Kotlin/Ktor/Exposed ecosystem#309cookiee339 wants to merge 10 commits intoaffaan-m:mainfrom
Conversation
Add comprehensive Kotlin ecosystem with 19 files: - 4 skills: kotlin-patterns, kotlin-testing, kotlin-ktor-patterns, kotlin-exposed-patterns - 2 agents: kotlin-reviewer, kotlin-build-resolver - 3 commands: kotlin-review, kotlin-build, kotlin-test - 5 rules (Claude Code): rules/kotlin/ - 5 rules (Cursor): .cursor/rules/kotlin-* Covers idiomatic Kotlin, coroutines, null safety, Ktor server, Exposed ORM, Kotest/MockK testing, Koin DI, and Kover coverage.
📝 WalkthroughWalkthroughAdds a comprehensive set of new Kotlin-focused documentation: Cursor-scoped rules/hooks, repo-level Kotlin rules, agent/role profiles, command workflows, and multiple SKILL knowledge pages. All changes are new Markdown docs; no executable code or public API changes. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
11 issues found across 19 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".cursor/rules/kotlin-security.md">
<violation number="1" location=".cursor/rules/kotlin-security.md:37">
P1: JWT `validate` block unconditionally returns `JWTPrincipal` without checking any claims — this is an insecure pattern, especially problematic in a security guidance document. Per the Ktor docs, the `validate` function should verify claims (e.g., audience, issuer, or custom claims) and return `null` for invalid tokens. The `verifier` should also include `.withAudience()` and `.withIssuer()` checks.</violation>
</file>
<file name="skills/kotlin-patterns/SKILL.md">
<violation number="1" location="skills/kotlin-patterns/SKILL.md:282">
P2: Misleading `supervisorScope` example: `await().getOrDefault()` doesn't catch async failures. `Deferred.await()` re-throws the coroutine's exception directly — it does not return `kotlin.Result<T>` — so `.getOrDefault(emptyList())` is unreachable on failure. This defeats the purpose of demonstrating `supervisorScope` for independent failure handling.
Wrap each `await()` in `runCatching` to actually handle child failures gracefully.</violation>
</file>
<file name="agents/kotlin-build-resolver.md">
<violation number="1" location="agents/kotlin-build-resolver.md:66">
P2: Avoid `rm -rf` on home-directory paths in agent instructions. This agent has `Bash` tool access and could execute this command automatically, clearing global Gradle caches that affect all projects on the machine. Existing agents in this repo only use `rm -rf` on project-local directories. Consider replacing with the project-scoped `./gradlew clean` (already present) or at minimum scoping to the project's local `.gradle` directory.</violation>
</file>
<file name="skills/kotlin-ktor-patterns/SKILL.md">
<violation number="1" location="skills/kotlin-ktor-patterns/SKILL.md:93">
P1: `call.inject<UserService>()` is not a valid Koin API. According to the Koin-Ktor documentation, `inject()` is available on `Application`, `Route`, and `Routing` — not on `ApplicationCall`. This example won't compile. The correct pattern (already shown later in the file at line 366) is `val userService by inject<UserService>()` at the `Route` scope.</violation>
<violation number="2" location="skills/kotlin-ktor-patterns/SKILL.md:476">
P1: Setting `maxFrameSize = Long.MAX_VALUE` removes any limit on WebSocket frame sizes, which can lead to denial-of-service via memory exhaustion from malicious clients. Use a reasonable production limit instead.</violation>
<violation number="3" location="skills/kotlin-ktor-patterns/SKILL.md:496">
P1: Iterating over a `Collections.synchronizedSet` with `forEach` without wrapping in a `synchronized(connections)` block is not thread-safe and can throw `ConcurrentModificationException`. The Java docs state: "It is imperative that the user manually synchronize on the returned set when iterating over it." In a coroutine context, consider using `ConcurrentHashMap.newKeySet()` or a `Mutex` instead.</violation>
</file>
<file name="commands/kotlin-review.md">
<violation number="1" location="commands/kotlin-review.md:70">
P2: Nested code blocks will break markdown rendering. The outer ` ```text ` fence is closed prematurely by the first inner ` ```kotlin ` fence on line 90. Use a longer outer fence (` ```` `) so the inner triple-backtick blocks are treated as literal content.
(Based on your team's feedback about using longer fences for nested code blocks.) [FEEDBACK_USED]</violation>
</file>
<file name="skills/kotlin-exposed-patterns/SKILL.md">
<violation number="1" location="skills/kotlin-exposed-patterns/SKILL.md:244">
P1: LIKE wildcard injection: user input is interpolated directly into the LIKE pattern without escaping SQL wildcard characters (`%`, `_`). While Exposed parameterizes the value (safe from SQL injection), a malicious query string like `%` or `_` would match all records. In a skill file teaching patterns, this should demonstrate proper sanitization. The same unsafe pattern is duplicated in `ExposedUserRepository.search` below.
Escape LIKE-special characters before interpolation, e.g.:
```kotlin
private fun String.escapeLike(): String =
this.replace("%", "\\%").replace("_", "\\_")
```</violation>
<violation number="2" location="skills/kotlin-exposed-patterns/SKILL.md:370">
P1: Type mismatch in DAO example: `CurrentTimestampWithTimeZone` is an `Expression<OffsetDateTime>`, but DAO entity properties expect concrete Kotlin values (e.g., `OffsetDateTime`). This compiles in DSL-style (`it[updatedAt] = expr`) but not in DAO-style property assignment. Use a concrete timestamp value instead.</violation>
</file>
<file name="skills/kotlin-testing/SKILL.md">
<violation number="1" location="skills/kotlin-testing/SKILL.md:785">
P2: The `codecov/codecov-action@v4` example is missing the required `token` input (a v4 breaking change — tokenless upload is no longer supported) and uses the deprecated `file` parameter (renamed to `files`). Users copying this CI snippet will get upload failures.</violation>
</file>
<file name="rules/kotlin/security.md">
<violation number="1" location="rules/kotlin/security.md:38">
P1: JWT `validate` block unconditionally accepts any token without checking claims. For a security guidance document, this sets a dangerous example. According to the Ktor documentation, the `validate` block should verify payload claims (e.g., audience, issuer) before returning a `JWTPrincipal`. Returning `null` rejects the token.</violation>
</file>
Since this is your first cubic review, here's how it works:
- cubic automatically reviews your code and comments on bugs and improvements
- Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
- Add one-off context when rerunning by tagging
@cubic-dev-aiwith guidance or docs links (includingllms.txt) - Ask questions if you need clarification on any suggestion
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 12
🧹 Nitpick comments (6)
.cursor/rules/kotlin-testing.md (1)
8-8: Hyphenate compound adjective.The phrase "Kotlin specific" should be hyphenated as "Kotlin-specific" when used as a compound adjective before a noun.
✏️ Proposed fix
-> This file extends the common testing rule with Kotlin specific content. +> This file extends the common testing rule with Kotlin-specific content.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.cursor/rules/kotlin-testing.md at line 8, Change the phrase "Kotlin specific" to the hyphenated compound adjective "Kotlin-specific" in the sentence "This file extends the common testing rule with Kotlin specific content." so it reads "This file extends the common testing rule with Kotlin-specific content."; update that exact string occurrence in the document..cursor/rules/kotlin-patterns.md (1)
8-8: Hyphenate compound adjective.The phrase "Kotlin specific" should be hyphenated as "Kotlin-specific" when used as a compound adjective before a noun.
✏️ Proposed fix
-> This file extends the common patterns rule with Kotlin specific content. +> This file extends the common patterns rule with Kotlin-specific content.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.cursor/rules/kotlin-patterns.md at line 8, Replace the unhyphenated compound adjective "Kotlin specific" with the hyphenated form "Kotlin-specific" throughout this file (e.g., in the sentence "This file extends the common patterns rule with Kotlin specific content.") so that compound adjectives before nouns are correctly hyphenated; search for the exact phrase "Kotlin specific" and update each occurrence to "Kotlin-specific".rules/kotlin/testing.md (1)
9-9: Hyphenate compound adjective.The phrase "Kotlin specific" should be hyphenated as "Kotlin-specific" when used as a compound adjective before a noun.
✏️ Proposed fix
-> This file extends [common/testing.md](../common/testing.md) with Kotlin specific content. +> This file extends [common/testing.md](../common/testing.md) with Kotlin-specific content.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@rules/kotlin/testing.md` at line 9, Change the compound adjective "Kotlin specific" to the hyphenated form "Kotlin-specific" in the sentence "This file extends [common/testing.md] ...", i.e., update the phrase inside the line that currently reads "This file extends [common/testing.md](../common/testing.md) with Kotlin specific content." to "This file extends [common/testing.md](../common/testing.md) with Kotlin-specific content."rules/kotlin/coding-style.md (1)
9-9: Hyphenate compound adjective.The phrase "Kotlin specific" should be hyphenated as "Kotlin-specific" when used as a compound adjective before a noun.
✏️ Proposed fix
-> This file extends [common/coding-style.md](../common/coding-style.md) with Kotlin specific content. +> This file extends [common/coding-style.md](../common/coding-style.md) with Kotlin-specific content.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@rules/kotlin/coding-style.md` at line 9, Replace the unhyphenated phrase "Kotlin specific" in the sentence "This file extends [common/coding-style.md](../common/coding-style.md) with Kotlin specific content." with the hyphenated compound adjective "Kotlin-specific" so the sentence reads "This file extends [common/coding-style.md](../common/coding-style.md) with Kotlin-specific content."skills/kotlin-ktor-patterns/SKILL.md (2)
156-166: Document security implications ofignoreUnknownKeys = true.While
ignoreUnknownKeys = true(line 161) is common for forward compatibility, it can mask security issues when deserializing untrusted input. Consider adding a note that additional validation should be performed on request data, especially for security-sensitive operations.📝 Suggested documentation addition
fun Application.configureSerialization() { install(ContentNegotiation) { json(Json { prettyPrint = true isLenient = false ignoreUnknownKeys = true encodeDefaults = true explicitNulls = false }) } } + +// Note: ignoreUnknownKeys = true allows forward compatibility but silently +// discards unknown fields. Always validate untrusted input explicitly in routes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skills/kotlin-ktor-patterns/SKILL.md` around lines 156 - 166, In configureSerialization() the JSON configuration sets ignoreUnknownKeys = true which can silently drop unexpected fields and mask malicious or malformed input; update SKILL.md to document this security implication and instruct developers to perform explicit validation and strict schema checks for security-sensitive endpoints (e.g., validate DTOs after deserialization, use manual field whitelisting or additional checks, or consider turning off ignoreUnknownKeys in trusted contexts), and reference the configureSerialization() block and the ignoreUnknownKeys setting so readers know where to look.
452-466: Inconsistent email validation pattern.Line 454 uses overly simplistic validation (
'@' in request.email), which accepts invalid emails like "@@" or "@Domain". This conflicts with the more robustEMAIL_REGEXpattern shown earlier in the document (line 162 in the other file context). Consider updating this example to use a proper regex or extracting validation to a reusable function.♻️ Suggested improvement
+private val EMAIL_REGEX = Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$") + fun CreateUserRequest.validate() { require(name.isNotBlank()) { "Name is required" } require(name.length <= 100) { "Name must be 100 characters or less" } - require('@' in email) { "Invalid email format" } + require(EMAIL_REGEX.matches(email)) { "Invalid email format" } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skills/kotlin-ktor-patterns/SKILL.md` around lines 452 - 466, The email checks use a simplistic '@' presence check in the request handler and in CreateUserRequest.validate() which allows invalid emails; replace these with the shared EMAIL_REGEX (or a single reusable function like isValidEmail(email: String): Boolean) and use that in both the handler (where request.email is validated before calling userService.create) and in CreateUserRequest.validate() so both validation sites reference the same robust regex/utility (update the require(...) calls to use EMAIL_REGEX.matches(email) or isValidEmail(email)).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.cursor/rules/kotlin-coding-style.md:
- Line 8: Change the prose phrase "Kotlin specific" to the hyphenated form
"Kotlin-specific" in the sentence that reads "This file extends the common
coding style rule with Kotlin specific content." so the line becomes "This file
extends the common coding style rule with Kotlin-specific content."; locate and
update that exact phrase ("Kotlin specific") accordingly.
In @.cursor/rules/kotlin-hooks.md:
- Line 8: Update the sentence "This file extends the common hooks rule with
Kotlin specific content." to use the hyphenated form by changing "Kotlin
specific" to "Kotlin-specific" so the line reads: "This file extends the common
hooks rule with Kotlin-specific content."; locate and edit that exact text in
.cursor/rules/kotlin-hooks.md.
In @.cursor/rules/kotlin-security.md:
- Line 8: Replace the phrase in the sentence "This file extends the common
security rule with Kotlin specific content." by changing "Kotlin specific" to
the hyphenated form "Kotlin-specific" so the line reads "This file extends the
common security rule with Kotlin-specific content."; update that exact string in
the .cursor/rules/kotlin-security.md content.
In `@agents/kotlin-build-resolver.md`:
- Around line 65-67: Replace the destructive global cache deletion line (`rm -rf
~/.gradle/caches/transforms-*`) with safer, project-scoped steps: keep
`./gradlew clean` and add guidance to use `./gradlew build
--refresh-dependencies` (or `./gradlew --refresh-dependencies`) for forcing
dependency refresh; mention any home-directory cache purge as an explicit manual
last-resort step rather than part of the automated resolver.
In `@commands/kotlin-test.md`:
- Around line 89-140: The tests access result.errors after calling
validateRegistration but result is typed as ValidationResult so you must
smart-cast or cast to ValidationResult.Invalid before accessing errors; update
each failing test (the ones creating RegistrationRequest and calling
validateRegistration in tests named "blank name returns Invalid", "invalid email
returns Invalid", "short password returns Invalid", and "multiple errors returns
all errors") to either use a when expression to handle ValidationResult and pull
the Invalid case or explicitly cast result to ValidationResult.Invalid (e.g.,
val invalid = result as ValidationResult.Invalid) before asserting on
invalid.errors.
In `@rules/kotlin/hooks.md`:
- Line 9: Update the phrase in the file that currently reads "Kotlin specific
content" (the sentence starting "This file extends [common/hooks.md]...") to use
the hyphenated compound adjective "Kotlin-specific content" so it reads "This
file extends [common/hooks.md] with Kotlin-specific content."
In `@rules/kotlin/patterns.md`:
- Line 9: Replace the phrase "Kotlin specific content" in the sentence "This
file extends [common/patterns.md](../common/patterns.md) with Kotlin specific
content." with the hyphenated form "Kotlin-specific content" to hyphenate the
compound adjective.
In `@rules/kotlin/security.md`:
- Line 9: Update the sentence in rules/kotlin/security.md that currently reads
"This file extends [common/security.md](../common/security.md) with Kotlin
specific content." to use the hyphenated form "Kotlin-specific content" for
consistency; locate the line containing that exact sentence and replace "Kotlin
specific content" with "Kotlin-specific content".
In `@skills/kotlin-exposed-patterns/SKILL.md`:
- Around line 24-50: The HikariCP default in DatabaseFactory.create currently
sets transactionIsolation = "TRANSACTION_REPEATABLE_READ"; update the docs
and/or code so the isolation level choice is consistent and justified: either
change the default in DatabaseFactory.create/DatabaseConfig to
"TRANSACTION_READ_COMMITTED" to match PostgreSQL defaults and the Transaction
Isolation examples, or keep REPEATABLE_READ but add a concise explanation in
SKILL.md that REPEATABLE_READ provides snapshot isolation, can surface
serialization anomalies requiring application-level retries, and include an
example usage and guidance on adding retry logic; also add REPEATABLE_READ to
the Transaction Isolation examples so the documentation and
DatabaseFactory.create config align.
In `@skills/kotlin-ktor-patterns/SKILL.md`:
- Around line 472-479: In Application.configureWebSockets(), replace the unsafe
production settings by capping maxFrameSize to a reasonable value (e.g., a few
megabytes) instead of Long.MAX_VALUE and enable masking (set masking = true);
also add a short comment above the WebSockets install explaining these are
production-recommended values that can be adjusted per deployment and link to
rationale (DoS/memory and masking protection) so future developers know why the
limits exist.
In `@skills/kotlin-patterns/SKILL.md`:
- Around line 489-540: Update the Kotlin and Kotest versions in the build
script: change both kotlin("jvm") and kotlin("plugin.serialization") plugin
versions from "2.3.0" to "2.3.10" (the plugin version strings at the top of the
file) and update the Kotest testImplementation coordinates
("io.kotest:kotest-runner-junit5" and "io.kotest:kotest-assertions-core" and
"io.kotest:kotest-property") from "6.1.3" to "6.1.4"; leave other dependency
versions (Ktor, Exposed, kotlinx-coroutines, mockk) unchanged. Ensure the
version strings match exactly where they appear in the plugins block and the
dependencies block so the build picks up Kotlin 2.3.10 and Kotest 6.1.4.
In `@skills/kotlin-testing/SKILL.md`:
- Around line 11-31: Update the SKILL.md top-level headings to match the
required format by adding explicit top-level sections named "When to Use", "How
It Works", and "Examples" (you can move/rename the existing "When to Activate"
-> "When to Use", "TDD Workflow for Kotlin" and "The RED-GREEN-REFACTOR Cycle"
-> into "How It Works", and the step-by-step content into "Examples" or split as
appropriate); ensure the existing bullets about writing Kotlin tests, TDD steps,
and Kover configuration remain unchanged semantically but are placed under the
new headings so the file conforms to the skills/**/*.md guideline.
---
Nitpick comments:
In @.cursor/rules/kotlin-patterns.md:
- Line 8: Replace the unhyphenated compound adjective "Kotlin specific" with the
hyphenated form "Kotlin-specific" throughout this file (e.g., in the sentence
"This file extends the common patterns rule with Kotlin specific content.") so
that compound adjectives before nouns are correctly hyphenated; search for the
exact phrase "Kotlin specific" and update each occurrence to "Kotlin-specific".
In @.cursor/rules/kotlin-testing.md:
- Line 8: Change the phrase "Kotlin specific" to the hyphenated compound
adjective "Kotlin-specific" in the sentence "This file extends the common
testing rule with Kotlin specific content." so it reads "This file extends the
common testing rule with Kotlin-specific content."; update that exact string
occurrence in the document.
In `@rules/kotlin/coding-style.md`:
- Line 9: Replace the unhyphenated phrase "Kotlin specific" in the sentence
"This file extends [common/coding-style.md](../common/coding-style.md) with
Kotlin specific content." with the hyphenated compound adjective
"Kotlin-specific" so the sentence reads "This file extends
[common/coding-style.md](../common/coding-style.md) with Kotlin-specific
content."
In `@rules/kotlin/testing.md`:
- Line 9: Change the compound adjective "Kotlin specific" to the hyphenated form
"Kotlin-specific" in the sentence "This file extends [common/testing.md] ...",
i.e., update the phrase inside the line that currently reads "This file extends
[common/testing.md](../common/testing.md) with Kotlin specific content." to
"This file extends [common/testing.md](../common/testing.md) with
Kotlin-specific content."
In `@skills/kotlin-ktor-patterns/SKILL.md`:
- Around line 156-166: In configureSerialization() the JSON configuration sets
ignoreUnknownKeys = true which can silently drop unexpected fields and mask
malicious or malformed input; update SKILL.md to document this security
implication and instruct developers to perform explicit validation and strict
schema checks for security-sensitive endpoints (e.g., validate DTOs after
deserialization, use manual field whitelisting or additional checks, or consider
turning off ignoreUnknownKeys in trusted contexts), and reference the
configureSerialization() block and the ignoreUnknownKeys setting so readers know
where to look.
- Around line 452-466: The email checks use a simplistic '@' presence check in
the request handler and in CreateUserRequest.validate() which allows invalid
emails; replace these with the shared EMAIL_REGEX (or a single reusable function
like isValidEmail(email: String): Boolean) and use that in both the handler
(where request.email is validated before calling userService.create) and in
CreateUserRequest.validate() so both validation sites reference the same robust
regex/utility (update the require(...) calls to use EMAIL_REGEX.matches(email)
or isValidEmail(email)).
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (19)
.cursor/rules/kotlin-coding-style.md.cursor/rules/kotlin-hooks.md.cursor/rules/kotlin-patterns.md.cursor/rules/kotlin-security.md.cursor/rules/kotlin-testing.mdagents/kotlin-build-resolver.mdagents/kotlin-reviewer.mdcommands/kotlin-build.mdcommands/kotlin-review.mdcommands/kotlin-test.mdrules/kotlin/coding-style.mdrules/kotlin/hooks.mdrules/kotlin/patterns.mdrules/kotlin/security.mdrules/kotlin/testing.mdskills/kotlin-exposed-patterns/SKILL.mdskills/kotlin-ktor-patterns/SKILL.mdskills/kotlin-patterns/SKILL.mdskills/kotlin-testing/SKILL.md
- JWT: add audience/issuer/subject claims validation
- Ktor: replace call.inject with Route-scoped by inject
- WebSocket: cap maxFrameSize at 64 KiB, add masking comment
- WebSocket: snapshot-under-lock for synchronizedSet iteration
- Exposed: escape %, _, \ in LIKE patterns to prevent wildcard injection
- Exposed: use Clock.System.now() instead of CurrentTimestampWithTimeZone in DAO
- Gradle: use project-local ./gradlew clean + .gradle/build-cache/
- Coroutines: wrap supervisorScope await in runCatching
- Tests: capture shouldBeInstanceOf return value before accessing .errors
- Markdown: use 4-backtick outer fence for nested code blocks
- CI: upgrade codecov-action to v5, add token, fix file→files key
- Testing skill: add How It Works section
- Ktor: use Regex(".+@.+\\..+") for email validation
- Gradle: add version-check comment to build.gradle.kts example
- HikariCP: use TRANSACTION_READ_COMMITTED instead of REPEATABLE_READ
- Style: hyphenate "Kotlin-specific" across all 11 rule files
There was a problem hiding this comment.
🧹 Nitpick comments (2)
skills/kotlin-exposed-patterns/SKILL.md (1)
11-18: Consider adding explicit top-level sections per skill guidelines.The skill includes "When to Activate" but is missing explicit "How It Works" and "Examples" top-level sections. While the content is comprehensive and well-organized with Database Setup, Table Definitions, DSL Queries, etc., the coding guidelines specify: "Skills should be formatted as Markdown with clear sections for When to Use, How It Works, and Examples." Consider adding these as top-level sections (you can alias "When to Activate" → "When to Use" and organize the current subsections under "How It Works" and "Examples").
As per coding guidelines: Skills should be formatted as Markdown with clear sections for When to Use, How It Works, and Examples.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skills/kotlin-exposed-patterns/SKILL.md` around lines 11 - 18, Rename the current top-level header "When to Activate" to "When to Use", and add two explicit top-level sections "How It Works" and "Examples"; move the existing subsections (Database Setup, Table Definitions, DSL Queries, HikariCP, Flyway, Repository pattern, JSON columns/complex queries) under "How It Works" as explanatory subsections and create a short "Examples" section that includes usage snippets or links to the DSL/DAO examples already present so the SKILL.md follows the required When to Use / How It Works / Examples structure.skills/kotlin-testing/SKILL.md (1)
11-28: Improved section structure, but still missing explicit "Examples" top-level section.The file now includes an explicit "How It Works" section (lines 19-28), which is an improvement over the past review. However, it still lacks a dedicated top-level "Examples" section as specified in the coding guidelines. While code examples are embedded throughout (TDD workflow, Kotest specs, MockK, etc.), consider adding an explicit "Examples" top-level section that either consolidates key examples or clearly delineates the examples section. You could also rename "When to Activate" to "When to Use" for exact guideline compliance.
As per coding guidelines: Skills should be formatted as Markdown with clear sections for When to Use, How It Works, and Examples.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skills/kotlin-testing/SKILL.md` around lines 11 - 28, Update the SKILL.md headings to match guidelines: rename the top-level heading "When to Activate" to "When to Use" and add a new top-level "Examples" section after "How It Works" that consolidates representative code snippets (a short Kotest spec example, a MockK mocking example, a small TDD RED/GREEN snippet, and the kover command example). Ensure the new "Examples" section is clearly labeled and contains concise, runnable snippets that reference the same concepts already in the doc so reviewers can find examples for StringSpec/FunSpec/BehaviorSpec, MockK usage, and the ./gradlew koverHtmlReport command.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@skills/kotlin-exposed-patterns/SKILL.md`:
- Around line 11-18: Rename the current top-level header "When to Activate" to
"When to Use", and add two explicit top-level sections "How It Works" and
"Examples"; move the existing subsections (Database Setup, Table Definitions,
DSL Queries, HikariCP, Flyway, Repository pattern, JSON columns/complex queries)
under "How It Works" as explanatory subsections and create a short "Examples"
section that includes usage snippets or links to the DSL/DAO examples already
present so the SKILL.md follows the required When to Use / How It Works /
Examples structure.
In `@skills/kotlin-testing/SKILL.md`:
- Around line 11-28: Update the SKILL.md headings to match guidelines: rename
the top-level heading "When to Activate" to "When to Use" and add a new
top-level "Examples" section after "How It Works" that consolidates
representative code snippets (a short Kotest spec example, a MockK mocking
example, a small TDD RED/GREEN snippet, and the kover command example). Ensure
the new "Examples" section is clearly labeled and contains concise, runnable
snippets that reference the same concepts already in the doc so reviewers can
find examples for StringSpec/FunSpec/BehaviorSpec, MockK usage, and the
./gradlew koverHtmlReport command.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
.cursor/rules/kotlin-coding-style.md.cursor/rules/kotlin-hooks.md.cursor/rules/kotlin-patterns.md.cursor/rules/kotlin-security.md.cursor/rules/kotlin-testing.mdagents/kotlin-build-resolver.mdcommands/kotlin-review.mdcommands/kotlin-test.mdrules/kotlin/coding-style.mdrules/kotlin/hooks.mdrules/kotlin/patterns.mdrules/kotlin/security.mdrules/kotlin/testing.mdskills/kotlin-exposed-patterns/SKILL.mdskills/kotlin-ktor-patterns/SKILL.mdskills/kotlin-patterns/SKILL.mdskills/kotlin-testing/SKILL.md
✅ Files skipped from review due to trivial changes (2)
- .cursor/rules/kotlin-testing.md
- skills/kotlin-ktor-patterns/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (6)
- .cursor/rules/kotlin-coding-style.md
- .cursor/rules/kotlin-hooks.md
- .cursor/rules/kotlin-patterns.md
- skills/kotlin-patterns/SKILL.md
- rules/kotlin/testing.md
- commands/kotlin-test.md
There was a problem hiding this comment.
3 issues found across 17 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="skills/kotlin-exposed-patterns/SKILL.md">
<violation number="1" location="skills/kotlin-exposed-patterns/SKILL.md:374">
P2: Type mismatch and inconsistency: `Clock.System.now()` returns `kotlinx.datetime.Instant` but the column is defined with `timestampWithTimeZone` (expects `java.time.OffsetDateTime`). Every other `updatedAt` assignment in this file uses `CurrentTimestampWithTimeZone` (the DB-side timestamp expression). This should match for consistency and to avoid application-vs-database clock skew.</violation>
</file>
<file name="skills/kotlin-patterns/SKILL.md">
<violation number="1" location="skills/kotlin-patterns/SKILL.md:282">
P1: Using `runCatching` with `await()` (a suspend function) swallows `CancellationException`, breaking structured concurrency. This is a known anti-pattern ([KT-W1066](https://deepsource.com/directory/kotlin/issues/KT-W1066), [kotlinx.coroutines#1814](https://github.com/Kotlin/kotlinx.coroutines/issues/1814)). In a best-practices skill file, this will teach readers an unsafe pattern.
Use an explicit `try`/`catch` that rethrows `CancellationException`, or show a coroutine-safe `suspendRunCatching` helper.</violation>
</file>
<file name="rules/kotlin/security.md">
<violation number="1" location="rules/kotlin/security.md:45">
P2: JWT `validate` block checks audience but not issuer, despite both being configured in the verifier. Since this is a security best-practices example, the validation should be consistent — either re-check both claims for defense-in-depth or note that the verifier already handles them. Missing the issuer check here may mislead developers following this guide.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
2 issues found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="skills/kotlin-ktor-patterns/SKILL.md">
<violation number="1" location="skills/kotlin-ktor-patterns/SKILL.md:478">
P1: This increases `maxFrameSize` from 64 KiB to 1 MB, contradicting the PR description which lists "WebSocket maxFrameSize 64 KiB" as a security hardening. A 1 MB frame limit makes the server more susceptible to memory-based denial-of-service. For a best-practices skill document, the previous conservative 64 KiB default with the cautionary comment was more appropriate.</violation>
<violation number="2" location="skills/kotlin-ktor-patterns/SKILL.md:479">
P2: Setting `masking = true` on the server side adds unnecessary XOR overhead on every outgoing frame. Per RFC 6455, masking is a client-to-server mechanism; server-to-client frames should not be masked. Ktor's own docs show `masking = false` as the default. The previous value (`false`) with the RFC explanation was correct.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
- Replace runCatching+await() anti-pattern with explicit try/catch that rethrows CancellationException to preserve structured concurrency - Fix Clock.System.now() type mismatch by using CurrentTimestampWithTimeZone for consistency with other updatedAt assignments - Add missing issuer check in JWT validate blocks for defense-in-depth - Update Kotlin 2.3.0 -> 2.3.10 and Kotest 6.1.3 -> 6.1.4 - Restructure kotlin-testing SKILL.md with required When to Use, How It Works, and Examples top-level sections
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
skills/kotlin-testing/SKILL.md (1)
81-113: Consolidate duplicated examples to avoid drift.The RED/GREEN email example and MockK basic mocking example are duplicated in multiple sections. Consider keeping one canonical example per topic and cross-linking to it.
Also applies to: 142-196, 49-79, 380-412
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skills/kotlin-testing/SKILL.md` around lines 81 - 113, Consolidate duplicated Kotlin examples by keeping one canonical implementation and test pair (the validateEmail function and EmailValidatorTest) and replace other copies with cross-references; specifically, retain the validateEmail(...) function and EmailValidatorTest class as the single source of truth, remove duplicated blocks elsewhere, and add internal links or references to that canonical section for other mentions (e.g., where the RED/GREEN or MockK examples currently repeat) so examples don't drift and maintenance stays centralized.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@skills/kotlin-exposed-patterns/SKILL.md`:
- Around line 11-19: Rename the top-level heading "When to Activate" to the
required "When to Use", and add the missing top-level headings "How It Works"
and "Examples" so the document follows the skills format; move the current
bullet list under the new "When to Use" heading, add a concise "How It Works"
section explaining Exposed + HikariCP/Flyway/DAO usage, and add at least one
small "Examples" snippet or referenced example note demonstrating Exposed
DSL/DAO usage and connection pooling; ensure headings are top-level (e.g., "#
When to Use", "# How It Works", "# Examples") and keep existing content but
reorganize under these headings.
In `@skills/kotlin-patterns/SKILL.md`:
- Around line 11-18: Rename the top-level "When to Activate" heading to "When to
Use" and reorganize the document to include top-level "How It Works" and
"Examples" headings; move or duplicate the existing bullets about
writing/reviewing/refactoring/configuring Kotlin under "When to Use", create a
"How It Works" section (can describe the skill's mechanics or triggers) and an
"Examples" section (add one or more usage examples or scenarios), ensuring the
SKILL.md follows the required top-level headings "When to Use", "How It Works",
and "Examples".
---
Nitpick comments:
In `@skills/kotlin-testing/SKILL.md`:
- Around line 81-113: Consolidate duplicated Kotlin examples by keeping one
canonical implementation and test pair (the validateEmail function and
EmailValidatorTest) and replace other copies with cross-references;
specifically, retain the validateEmail(...) function and EmailValidatorTest
class as the single source of truth, remove duplicated blocks elsewhere, and add
internal links or references to that canonical section for other mentions (e.g.,
where the RED/GREEN or MockK examples currently repeat) so examples don't drift
and maintenance stays centralized.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
.cursor/rules/kotlin-security.mdrules/kotlin/security.mdskills/kotlin-exposed-patterns/SKILL.mdskills/kotlin-patterns/SKILL.mdskills/kotlin-testing/SKILL.md
✅ Files skipped from review due to trivial changes (1)
- .cursor/rules/kotlin-security.md
🚧 Files skipped from review as they are similar to previous changes (1)
- rules/kotlin/security.md
There was a problem hiding this comment.
1 issue found across 5 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="skills/kotlin-exposed-patterns/SKILL.md">
<violation number="1" location="skills/kotlin-exposed-patterns/SKILL.md:374">
P1: `CurrentTimestampWithTimeZone` is an SQL `Expression`/`Function` object, not an actual timestamp value. In the DAO pattern, entity property delegates expect the mapped Kotlin type (e.g., `Instant`), so this assignment won't compile. The original `Clock.System.now()` was correct for DAO usage. (`CurrentTimestampWithTimeZone` is valid in DSL-style updates like `it[updatedAt] = CurrentTimestampWithTimeZone`, which this file already uses correctly elsewhere.)</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
- Rename "When to Activate" to "When to Use" in kotlin-exposed-patterns and kotlin-patterns - Add "How It Works" and "Examples" top-level sections to both files - Deduplicate kotlin-testing examples by replacing full code blocks with cross-references to canonical sections
CurrentTimestampWithTimeZone is an SQL expression that works in DSL updates but not in DAO property assignments where the delegated property expects a concrete OffsetDateTime value.
There was a problem hiding this comment.
1 issue found across 4 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="skills/kotlin-patterns/SKILL.md">
<violation number="1" location="skills/kotlin-patterns/SKILL.md:21">
P3: The description says "six key areas" but lists seven. Either update the count to "seven" or merge/remove one item to match.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
skills/kotlin-exposed-patterns/SKILL.md (1)
537-545: Avoid nested repository calls inside a transaction example.At Line 544,
findById(id.value)from insidenewSuspendedTransactionis a misleading pattern for Exposed docs (extra transaction boundary and unnecessary second round-trip). Prefer mapping from the inserted row directly.Suggested example refinement
override suspend fun create(request: CreateUserRequest): User = newSuspendedTransaction(db = database) { - val id = UsersTable.insertAndGetId { - it[name] = request.name - it[email] = request.email - it[role] = request.role - } - findById(id.value)!! + val row = UsersTable.insert { + it[name] = request.name + it[email] = request.email + it[role] = request.role + }.resultedValues!!.single() + row.toUser() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skills/kotlin-exposed-patterns/SKILL.md` around lines 537 - 545, The create suspend function currently performs an extra repository lookup by calling findById(id.value) inside newSuspendedTransaction, causing an extra transaction boundary and round-trip; instead, after inserting via UsersTable.insertAndGetId (or use UsersTable.insert) map the inserted row/insert statement result directly to a User and return it within the same newSuspendedTransaction block—replace the findById call with direct mapping logic that builds the User from the inserted columns (name, email, role, id) in the create(CreateUserRequest) implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@skills/kotlin-exposed-patterns/SKILL.md`:
- Around line 591-594: In valueFromDB (the JSONB decoding branch) avoid the
unsafe non-null assertion on PGobject.value; instead read PGobject.value into a
local nullable variable, explicitly check for null and throw an
IllegalArgumentException (or other clear error) with a descriptive message if it
is null, otherwise pass the non-null string to json.decodeFromString; update the
PGobject branch in valueFromDB to perform this null-check rather than using !!
so callers see a clear error instead of an NPE.
In `@skills/kotlin-testing/SKILL.md`:
- Around line 680-692: The README currently uses the macOS-only `open` command
to view the Kover HTML report; update SKILL.md so the instructions are
cross-platform by replacing or augmenting the `open
build/reports/kover/html/index.html` line with OS-specific alternatives (e.g.,
`xdg-open` for Linux, `start` for Windows) or a portable suggestion (e.g., "open
the generated file in your browser" or use a tiny Java/Gradle task to serve the
report). Reference the existing gradle task names (koverHtmlReport, koverVerify,
koverXmlReport) and ensure the doc block lists alternatives for macOS, Linux,
and Windows so users can open the HTML report regardless of OS.
---
Nitpick comments:
In `@skills/kotlin-exposed-patterns/SKILL.md`:
- Around line 537-545: The create suspend function currently performs an extra
repository lookup by calling findById(id.value) inside newSuspendedTransaction,
causing an extra transaction boundary and round-trip; instead, after inserting
via UsersTable.insertAndGetId (or use UsersTable.insert) map the inserted
row/insert statement result directly to a User and return it within the same
newSuspendedTransaction block—replace the findById call with direct mapping
logic that builds the User from the inserted columns (name, email, role, id) in
the create(CreateUserRequest) implementation.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
skills/kotlin-exposed-patterns/SKILL.mdskills/kotlin-ktor-patterns/SKILL.mdskills/kotlin-patterns/SKILL.mdskills/kotlin-testing/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (2)
- skills/kotlin-patterns/SKILL.md
- skills/kotlin-ktor-patterns/SKILL.md
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
- Replace unsafe !! on PGobject.value with explicit null check and descriptive error message in JSONB column type - Add Linux/Windows alternatives for viewing Kover HTML report - Map insert result directly in create() instead of extra findById call
|
@coderabbitai resume |
✅ Actions performedReviews resumed. |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
skills/kotlin-testing/SKILL.md (2)
437-455: Consider adding missing imports for completeness.The debounce testing example uses
MutableSharedFlowandlaunchwithout showing imports. While experienced developers will recognize these, adding the imports would make the example more self-contained for learners.📚 Suggested addition
import io.kotest.matchers.collections.shouldContainInOrder import kotlinx.coroutines.flow.toList import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.launch class FlowServiceTest : FunSpec({🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skills/kotlin-testing/SKILL.md` around lines 437 - 455, The example is missing imports for coroutine/test types used in the debounce test; add the appropriate imports so the code is self-contained (e.g., imports for MutableSharedFlow, launch, runTest, advanceTimeBy, and any test coroutine scope utilities) and ensure SearchService and User types are resolved in the example's context so the test compiles and runs as-is.
509-517: Consider adding a note about serialization requirements.The property testing example for serialization roundtrip (line 513) uses
Json.encodeToStringwithout showing the required import or that theUserclass needs the@Serializableannotation. A brief note would help readers understand the prerequisites.📝 Suggested addition
Add a comment before the test:
+ // Requires: import kotlinx.serialization.json.Json + // and `@Serializable` annotation on User class test("serialization roundtrip preserves data") { checkAll(Arb.bind(Arb.string(1..50), Arb.string(5..100)) { name, email ->🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skills/kotlin-testing/SKILL.md` around lines 509 - 517, Add a short explanatory comment before the test("serialization roundtrip preserves data") explaining that Json.encodeToString/Json.decodeFromString require kotlinx.serialization and that the data class User must be annotated with `@Serializable` (and appropriate imports for kotlinx.serialization.json.Json and kotlinx.serialization.Serializable must be present), so readers know the prerequisites for the example to compile and run.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@skills/kotlin-testing/SKILL.md`:
- Around line 437-455: The example is missing imports for coroutine/test types
used in the debounce test; add the appropriate imports so the code is
self-contained (e.g., imports for MutableSharedFlow, launch, runTest,
advanceTimeBy, and any test coroutine scope utilities) and ensure SearchService
and User types are resolved in the example's context so the test compiles and
runs as-is.
- Around line 509-517: Add a short explanatory comment before the
test("serialization roundtrip preserves data") explaining that
Json.encodeToString/Json.decodeFromString require kotlinx.serialization and that
the data class User must be annotated with `@Serializable` (and appropriate
imports for kotlinx.serialization.json.Json and
kotlinx.serialization.Serializable must be present), so readers know the
prerequisites for the example to compile and run.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
skills/kotlin-exposed-patterns/SKILL.mdskills/kotlin-patterns/SKILL.mdskills/kotlin-testing/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (2)
- skills/kotlin-patterns/SKILL.md
- skills/kotlin-exposed-patterns/SKILL.md
…ples - Add MutableSharedFlow, launch, advanceTimeBy imports to Flow test - Add kotlinx.serialization imports and @serializable prerequisite note to property-based testing example
There was a problem hiding this comment.
🧹 Nitpick comments (1)
skills/kotlin-testing/SKILL.md (1)
41-824: Consider nesting detailed content underExamplesfor clearer hierarchy.From Line 41 onward, most sections are still top-level (
##). Making them###under## Exampleswould improve scanability and keep the document structure tighter.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@skills/kotlin-testing/SKILL.md` around lines 41 - 824, The document uses many top-level headings from "TDD Workflow for Kotlin" onward; group these under a new "## Examples" section by converting those subsequent top-level headings (e.g., "TDD Workflow for Kotlin", "Kotest Spec Styles", "Kotest Matchers", "MockK", "Coroutine Testing", "Property-Based Testing", "Data-Driven Testing", "Test Lifecycle and Fixtures", "Kotest Extensions", "Kover Coverage", "Ktor testApplication Testing", etc.) into third-level headings (###) nested beneath a newly inserted "## Examples" heading; update the heading levels consistently throughout SKILL.md and adjust any internal anchors or TOC entries to match the new hierarchy.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@skills/kotlin-testing/SKILL.md`:
- Around line 41-824: The document uses many top-level headings from "TDD
Workflow for Kotlin" onward; group these under a new "## Examples" section by
converting those subsequent top-level headings (e.g., "TDD Workflow for Kotlin",
"Kotest Spec Styles", "Kotest Matchers", "MockK", "Coroutine Testing",
"Property-Based Testing", "Data-Driven Testing", "Test Lifecycle and Fixtures",
"Kotest Extensions", "Kover Coverage", "Ktor testApplication Testing", etc.)
into third-level headings (###) nested beneath a newly inserted "## Examples"
heading; update the heading levels consistently throughout SKILL.md and adjust
any internal anchors or TOC entries to match the new hierarchy.
Summary
Add comprehensive Kotlin ecosystem support with 19 files covering idiomatic Kotlin, Ktor server framework, JetBrains Exposed ORM, Kotest/MockK testing, Koin DI, and Kover coverage.
Files Added
Skills (4)
skills/kotlin-patterns/SKILL.md— Idiomatic Kotlin: coroutines, null safety, sealed classes, extension functions, DSL buildersskills/kotlin-testing/SKILL.md— Kotest (FunSpec, BehaviorSpec), MockK, coroutine testing, property-based testing, Kover coverageskills/kotlin-ktor-patterns/SKILL.md— Ktor server: routing DSL, plugins, authentication, Koin DI, kotlinx.serialization, WebSockets, testApplicationskills/kotlin-exposed-patterns/SKILL.md— JetBrains Exposed ORM: DSL queries, DAO pattern, transactions, HikariCP, Flyway migrations, repository patternAgents (2)
agents/kotlin-reviewer.md— Kotlin code reviewer for idiomatic patterns, null safety, coroutine safety, and securityagents/kotlin-build-resolver.md— Kotlin/Gradle build error resolver for compiler errors, dependency issues, and Gradle configCommands (3)
commands/kotlin-review.md—/kotlin-reviewslash command to invoke the kotlin-reviewer agentcommands/kotlin-build.md—/kotlin-buildslash command to invoke the kotlin-build-resolver agentcommands/kotlin-test.md—/kotlin-testslash command to enforce TDD with Kotest/KoverRules — Claude Code (5)
rules/kotlin/coding-style.md— Kotlin coding conventions, naming, immutability, coroutine patternsrules/kotlin/patterns.md— Architecture patterns: repository, service layer, Ktor routing, Exposed DAOsrules/kotlin/testing.md— Testing standards: Kotest, MockK, coroutine test dispatchers, 80%+ coveragerules/kotlin/security.md— Security: input validation, SQL injection prevention, coroutine cancellation safetyrules/kotlin/hooks.md— Hook configurations for Kotlin/Gradle projects (detekt, ktlint, build checks)Rules — Cursor (5)
.cursor/rules/kotlin-coding-style.md.cursor/rules/kotlin-patterns.md.cursor/rules/kotlin-testing.md.cursor/rules/kotlin-security.md.cursor/rules/kotlin-hooks.mdType
Testing
/kotlin-review,/kotlin-build,/kotlin-test)Design Notes
Checklist
Summary by cubic
Adds a full Kotlin ecosystem (Ktor, Exposed, Kotest) with skills, agents, commands, and rules. Hardens security, improves build/test tooling and docs, and adds JSONB null-safety plus a small query optimization.
New Features
Bug Fixes
Written for commit 5cacc96. Summary will update on new commits.
Summary by CodeRabbit