8.8 KiB
Phase 1: Project Infrastructure & Module Wiring - Discussion Log
Audit trail only. Do not use as input to planning, research, or execution agents. Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
Date: 2026-04-24 Phase: 01-project-infrastructure-module-wiring Areas discussed: Target matrix, Convention plugin split, Code-quality toolchain, "Running-but-empty" scope
Target matrix
Q1: JS target in composeApp + shared — drop it?
| Option | Description | Selected |
|---|---|---|
| Drop js, keep wasmJs | PROJECT.md mentions Wasm but not js; js is legacy Kotlin/JS path | ✓ |
| Keep both js and wasmJs | Preserve template exactly; zero risk of Kotlin/JS regression on future merges | |
| Drop both | Strictest minimum; re-add wasmJs only if web becomes real |
User's choice: Drop js, keep wasmJs.
Q2: iosX64 target — include?
| Option | Description | Selected |
|---|---|---|
| Skip iosX64 | User is on Apple Silicon; iosArm64 + iosSimulatorArm64 sufficient | ✓ |
| Add iosX64 for safety | Intel-Mac safety net; costs a second iOS compile per build |
User's choice: Skip iosX64.
Q3: Desktop JVM target — role?
| Option | Description | Selected |
|---|---|---|
| Dev-tool only, no shipped artifact | Hot-reload loop retained; no packaging; PROJECT.md alignment | ✓ |
| Full desktop app, packaged + shipped | dmg/msi/exe as release surface; out of PROJECT.md scope | |
| Drop desktop target entirely | Simplest; loses hot reload |
User's choice: Dev-tool only, no shipped artifact.
Q4: shared/ target set — mirror composeApp?
| Option | Description | Selected |
|---|---|---|
| Mirror composeApp exactly | Same target set; consistent dep graph | ✓ |
| Superset: ship everything KMP supports | Every target just in case; build cost | |
| Minimum: only what's used today | Strictest diet; same as Option 1 if we drop js |
User's choice: Mirror composeApp exactly.
Convention plugin split
Q1: How granular should the convention plugins be?
| Option | Description | Selected |
|---|---|---|
| Fine-grained (4–5 plugins) | recipe.kotlin.multiplatform + .compose.multiplatform + .android.application + .jvm.server + .quality | ✓ |
| Coarse (2 plugins) | recipe.kmp + recipe.server; leaks Compose config into shared | |
| Monolith (1 plugin) | Single recipe.conventions with conditional logic |
User's choice: Fine-grained (4–5 plugins). Preview showed the role-declaration pattern in each module's plugins block.
Q2: What does the KMP convention plugin lock in?
| Option | Description | Selected |
|---|---|---|
| Targets + toolchain + common test deps | New KMP module = apply plugin, done | ✓ |
| Targets + toolchain only | Thinner plugin, more repetition downstream | |
| Everything incl. Koin + Kermit wiring | Upfront convenience, invasive over time |
User's choice: Targets + toolchain + common test deps.
Q3: JVM toolchain version?
| Option | Description | Selected |
|---|---|---|
| JVM 21 everywhere, androidTarget stays JVM 11 | Split kept; modern JDK on server, Android constrained | ✓ |
| JVM 17 everywhere | Unified; loses JVM-21 features (virtual threads) | |
| Keep template defaults | Zero refactor risk; loses explicit control |
User's choice: JVM 21 everywhere, androidTarget stays JVM 11.
Q4: Where do library version strings live?
| Option | Description | Selected |
|---|---|---|
| All versions in libs.versions.toml, nowhere else | Strict INFRA-01 SC#2 | ✓ |
| Catalog for libs, plugin versions inline | Technically violates SC#2 |
User's choice: All versions in libs.versions.toml, nowhere else.
Code-quality toolchain
User clarified: "What are Detekt alternatives? Is ktlint OK?" Discussion explained Detekt = static analysis, ktlint = formatting — not alternatives, usually paired. Presented tiers (minimal / standard / architecture-aware) and user chose minimal.
Q1: Static analysis (Detekt)?
| Option | Description | Selected |
|---|---|---|
| Wire Detekt now | Default ruleset + baseline; catches Kotlin footguns | |
| Skip Detekt; lean on IDE + compiler | No CI gate for static analysis | ✓ |
| Placeholder task, no rules | Wire-in-place for future enablement |
User's choice: Skip Detekt. Minimal baseline. Notes: Konsist (architecture fitness) deferred to ~Phase 4 when SyncEngine rules exist.
Q2: Formatting / linting?
| Option | Description | Selected |
|---|---|---|
| ktlint via Spotless plugin | One tool: Kotlin + Gradle + markdown | ✓ |
| ktlint plugin directly | Thinner; loses multi-format coverage | |
| Skip, rely on IDE + .editorconfig | No CI-level gate |
User's choice: ktlint via Spotless plugin.
Q3: Compiler warnings as errors?
| Option | Description | Selected |
|---|---|---|
| allWarningsAsErrors = true everywhere | Max discipline; deprecations force conscious suppression | ✓ |
| Warn only | Noise accumulates | |
| As-errors for module code, relaxed for generated | Small config carve-out |
User's choice: allWarningsAsErrors = true everywhere.
Q4: Explicit API mode for shared/?
User clarified: "I don't understand it. What is this explicit api?" and later "Is this some kind of a standard because I am writing kotlin server applications and didn't meet with that". Discussion explained explicitApi as a library-authoring convention (stdlib, coroutines, Ktor etc.) requiring public keyword + explicit return types. User weighed the tradeoff and picked strict-on-shared/ on library-contract grounds.
| Option | Description | Selected |
|---|---|---|
| Skip entirely | Kotlin defaults; no public ceremony |
|
| Strict on shared/ only | Library discipline on the cross-runtime contract | ✓ |
User's choice: Strict on shared/ only.
Q5: Git hooks?
| Option | Description | Selected |
|---|---|---|
| No git hooks | ./gradlew check is the gate; CI later |
✓ |
| Pre-commit hook running spotlessCheck | Blocks commits with formatting drift |
User's choice: No git hooks.
"Running-but-empty" scope
Q1: Koin DI bootstrap — wire it in Phase 1?
| Option | Description | Selected |
|---|---|---|
| Wire minimal bootstrap now | Empty appModule + startKoin in App() and MainViewController | ✓ |
| Defer to Phase 2 | Phase 2 does DI + auth together |
User's choice: Wire minimal bootstrap now.
Q2: Kermit logger bootstrap?
| Option | Description | Selected |
|---|---|---|
| Set up Kermit now | Logger available from day 1 | ✓ |
| Defer | Add when first feature needs logging |
User's choice: Set up Kermit now.
Q3: Server "running-but-empty" — /health + Flyway scaffold + Postgres config?
| Option | Description | Selected |
|---|---|---|
| Health endpoint + Flyway scaffold + Postgres conn config | Phase 3 migrations drop into an already-wired migrator | ✓ |
| Health endpoint only, no DB | Phase 3 wires Flyway + Postgres together | |
| Strictly the template skeleton | Most minimal; Phase 2 and 3 do more |
User's choice: Health endpoint + Flyway scaffold + Postgres conn config.
Q4: docker-compose.yml in Phase 1?
| Option | Description | Selected |
|---|---|---|
| Add docker-compose.yml now | Phase 3 doesn't have to litigate local-Postgres setup | ✓ |
| Defer to Phase 3 | Compose arrives with first migration | |
| No compose; use homelab Postgres directly | Fastest setup; dev pollutes shared instance |
User's choice: Add docker-compose.yml now.
Claude's Discretion
- Exact ordering of plugin application inside each
build.gradle.kts - Specific Spotless ktlint ruleset version (pick latest stable from catalog)
- Whether
application.confor a Kotlin config class owns env-var parsing - Flyway
cleanDisabled/baselineOnMigrateflag choices - iOS Koin bootstrap idiom (
KoinApplicationvsstartKoinin MainViewController) docker-compose.ymlshape:.envfile vs inline localhost defaults- Exact sentinel JSON body for
/health
Deferred Ideas
- Detekt static analysis — revisit only if review misses start compounding
- Konsist architecture fitness tests — revisit ~Phase 4 (SyncEngine rules)
- CI pipeline — Phase 11 (deployment)
- Git hooks — considered; revisit only on recurring format drift
- explicitApi for composeApp / server — rejected (app code, not libraries)
- iosX64 target — rejected (no Intel-Mac contributors)
jstarget — rejected (wasmJs covers future-web intent)- Compose Desktop packaging (dmg/msi/exe) — Desktop is dev-only