Files
recipe/.planning/phases/01-project-infrastructure-module-wiring/01-DISCUSSION-LOG.md
2026-04-29 21:07:49 +02:00

8.8 KiB
Raw Blame History

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 (45 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 (45 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.conf or a Kotlin config class owns env-var parsing
  • Flyway cleanDisabled / baselineOnMigrate flag choices
  • iOS Koin bootstrap idiom (KoinApplication vs startKoin in MainViewController)
  • docker-compose.yml shape: .env file 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)
  • js target — rejected (wasmJs covers future-web intent)
  • Compose Desktop packaging (dmg/msi/exe) — Desktop is dev-only