# 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.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
