57 lines
3.1 KiB
Markdown
57 lines
3.1 KiB
Markdown
---
|
||
name: "Adr 0002 Kotlin Choice"
|
||
description: "Decision to implement in Kotlin"
|
||
depth: 2
|
||
links: ["../index.md", "./adr-0000-invariants.md"]
|
||
---
|
||
|
||
# ADR 0002: use kotlin as the primary implementation language
|
||
|
||
**status:** accepted
|
||
**date:** 07.05.2026 (May)
|
||
**deciders:** Kami
|
||
|
||
---
|
||
|
||
## context
|
||
|
||
Correx is a state‑heavy, event‑heavy, concurrency‑heavy orchestration runtime. The language choice affects maintainability, type safety, concurrency model, and ecosystem compatibility for a local‑first tool.
|
||
|
||
## decision
|
||
|
||
We implement the core, orchestration, and infrastructure layers in **Kotlin** (JVM), using the following primary libraries:
|
||
|
||
* **coroutines** – structured concurrency
|
||
* **kotlinx.serialization** – event/artifact serialization
|
||
* **Exposed** – type‑safe SQL for persistence
|
||
* **Ktor** – web/API server
|
||
* **Hoplite** / Typesafe Config – configuration
|
||
|
||
Frontend interface uses **SvelteKit**; CLI uses **Clikt**.
|
||
|
||
## rationale
|
||
|
||
1. **Sealed hierarchies and data classes** map naturally to event types, artifact schemas, and validation outcomes.
|
||
2. **Coroutines** provide safe, cancellable concurrency that aligns with the session lifecycle (cancellation tokens, explicit scopes).
|
||
3. **Immutability by default** reduces accidental state mutation – critical for event sourcing.
|
||
4. **Type‑safe DSLs** enable clean definition of transitions, conditions, and tool contracts directly in config or code.
|
||
5. The existing team knows Java/Kotlin, reducing ramp‑up.
|
||
6. Libraries like `kotlinx.serialization` give deterministic, reflection‑free serialization.
|
||
7. Strong typing prevents many categories of bugs that would be hard to catch in a dynamic language when dealing with complex state machines.
|
||
|
||
## alternatives considered
|
||
|
||
* **Python**: excellent for glue and rapid prototyping, but dynamic typing and global interpreter lock make deterministic concurrency, state integrity, and long‑term maintenance riskier. Python was used in the failed `llm-pipeline` prototype and contributed to hidden mutable state issues.
|
||
* **TypeScript (Node.js)**: single‑threaded event loop simplifies some concurrency but lacks the type‑safety and structured concurrency needed for complex orchestration state machines. Validation and serialization would rely on third‑party libraries with varying degrees of strictness.
|
||
* **Rust**: performance and safety are excellent, but the borrow checker and ecosystem immaturity for LLM tooling would slow development significantly.
|
||
|
||
## consequences
|
||
|
||
* JVM startup overhead; acceptable for a tool that runs as a persistent server or long‑lived CLI.
|
||
* Requires Kotlin knowledge; mitigated by good documentation and a growing community.
|
||
* Interop with native libraries (e.g., llama.cpp) will be via JNI or external processes; we accept that complexity and encapsulate it in infrastructure providers.
|
||
* We must enforce module dependency rules (with tools like ArchUnit) to prevent core from depending on infrastructure.
|
||
|
||
## status
|
||
|
||
This decision is foundational and aligns with the architectural need for a “workflow engine” rather than a “scripting environment”. |