---
phase: 01-project-infrastructure-module-wiring
plan: 02
subsystem: infra
tags: [gradle, build-logic, included-build, precompiled-plugins, version-catalog, kotlin-multiplatform, compose, ktor, spotless, flyway, pitfall-1, pitfall-2, pitfall-9, pitfall-10]

requires: [01-01]
provides:
  - "build-logic/ included build resolving the parent catalog via files(\"../gradle/libs.versions.toml\")"
  - "Precompiled plugin recipe.quality (Spotless + ktlint + D-11 allWarningsAsErrors safety net via plugins.withId guard)"
  - "Precompiled plugin recipe.kotlin.multiplatform (D-05 target matrix: androidTarget, iosArm64, iosSimulatorArm64, jvm, wasmJs; JVM toolchain 21 + JVM 11 Android bytecode per D-08; framework baseName = ComposeApp; Koin BOM + koin-core + Kermit + kotlin-test common deps; allWarningsAsErrors at kotlin{} level)"
  - "Precompiled plugin recipe.compose.multiplatform (layers on recipe.kotlin.multiplatform — PITFALL #2 avoided; Compose + composeCompiler + composeHotReload + commonMain Compose deps + lifecycle-viewmodel-compose + koin-compose)"
  - "Precompiled plugin recipe.android.application (namespace dev.ulfrx.recipe; findVersion catalog accessor per PITFALL #1; SDK versions from catalog)"
  - "Precompiled plugin recipe.jvm.server (Kotlin JVM + Ktor + Flyway + application; quoted \"implementation\" configs; cleanDisabled=true; D-08 JVM toolchain 21)"
  - "Root settings.gradle.kts with includeBuild(\"build-logic\") placed inside pluginManagement{} (PITFALL #9)"
  - "Root build.gradle.kts with 10 alias(...) apply false entries (8 existing + Spotless + Flyway classloader hints)"
affects: [01-03, 01-04, 01-05, 01-06, 01-07]

tech-stack:
  added:
    - "build-logic/ included build (kotlin-dsl convention-plugin project)"
    - "5 precompiled script plugins: recipe.quality, recipe.kotlin.multiplatform, recipe.compose.multiplatform, recipe.android.application, recipe.jvm.server"
  patterns:
    - "PITFALL #1 mitigation: every precompiled plugin reads versions via extensions.getByType<VersionCatalogsExtension>().named(\"libs\")"
    - "PITFALL #2 mitigation: recipe.compose.multiplatform applies id(\"recipe.kotlin.multiplatform\") — KMP plugin applied transitively"
    - "PITFALL #9 mitigation: includeBuild(\"build-logic\") sits inside pluginManagement{}"
    - "PITFALL #10 mitigation: baseName = \"ComposeApp\" set on both iOS frameworks"
    - "Quoted-configuration footgun avoidance: recipe.jvm.server uses \"implementation\"(...) string-literal configs"
    - "D-11 redundancy guard: recipe.quality uses plugins.withId guards for composability"
    - "Plugin coordinate synthesis via Provider<PluginDependency>.asDependency() keeps build-logic/build.gradle.kts catalog-only"

key-files:
  created:
    - build-logic/settings.gradle.kts
    - build-logic/build.gradle.kts
    - build-logic/src/main/kotlin/recipe.quality.gradle.kts
    - build-logic/src/main/kotlin/recipe.kotlin.multiplatform.gradle.kts
    - build-logic/src/main/kotlin/recipe.compose.multiplatform.gradle.kts
    - build-logic/src/main/kotlin/recipe.android.application.gradle.kts
    - build-logic/src/main/kotlin/recipe.jvm.server.gradle.kts
  modified:
    - settings.gradle.kts
    - build.gradle.kts

key-decisions:
  - "Content for all 7 build-logic/ files copied verbatim from 01-RESEARCH.md § Code Examples + 01-PATTERNS.md; no structural changes."
  - "9 compileOnly(...asDependency()) entries omit androidLibrary — no recipe-family precompiled plugin applies com.android.library; shared/build.gradle.kts applies that plugin directly in 01-03."
  - "recipe.quality's D-11 safety net is plugins.withId-guarded so the plugin remains composable when applied standalone."

patterns-established:
  - "Pattern 1 (role declarations): each recipe.* plugin encodes a module role; shared/ cannot pull Compose transitively (INFRA-06)."
  - "Pattern 2 (catalog-only versioning inside build-logic): plugin coordinates via asDependency(); library refs via findLibrary; version refs via findVersion.toString().toInt()."
  - "Pattern 3 (Flyway CLI + runtime split): flyway{} block for CLI ergonomics; runtime migration handled in 01-05."
  - "Pattern 4 (JVM target split): jvmToolchain(21) drives shared/server/desktop; Android bytecode pinned at JVM 11; server JVM output at JVM 21."

requirements-completed: [INFRA-02]

duration: ~5min
completed: 2026-04-24
tasks-completed: 2
files-created: 7
files-modified: 2
---

# Phase 01 Plan 02: build-logic included build + 5 precompiled script plugins

`build-logic/` scaffolded as an included build whose 5 precompiled script plugins encode D-05/D-06/D-08/D-11/D-20 constraints once, and whose single hook into the root project is `includeBuild("build-logic")` inside `settings.gradle.kts pluginManagement { }` per PITFALL #9.

## What was built

- **`build-logic/settings.gradle.kts`** — resolves parent catalog via `from(files("../gradle/libs.versions.toml"))`; `rootProject.name = "build-logic"`.
- **`build-logic/build.gradle.kts`** — applies `` `kotlin-dsl` ``; 9 `compileOnly(libs.plugins.*.asDependency())` entries; `Provider<PluginDependency>.asDependency()` extension synthesises coordinates.
- **`recipe.quality.gradle.kts`** — Spotless + ktlint on `src/**/*.kt` with `targetExclude("**/build/**", "**/generated/**")`; two `plugins.withId` guards enforce `allWarningsAsErrors.set(true)` on `KotlinCompilationTask<*>` when a Kotlin plugin is present.
- **`recipe.kotlin.multiplatform.gradle.kts`** — canonical KMP plugin; `jvmToolchain(21)`; `androidTarget { jvmTarget = JVM_11 }`; `iosArm64()` + `iosSimulatorArm64()` with `baseName = "ComposeApp"; isStatic = true`; `jvm { jvmTarget = JVM_21 }`; `wasmJs { browser() }`; commonMain deps: Koin BOM + koin-core + Kermit; commonTest: kotlin-test.
- **`recipe.compose.multiplatform.gradle.kts`** — applies `id("recipe.kotlin.multiplatform")` (PITFALL #2) + compose MP + compose compiler + compose hot-reload; commonMain Compose deps + lifecycle-viewmodel + koin-compose.
- **`recipe.android.application.gradle.kts`** — `namespace = "dev.ulfrx.recipe"`; SDK versions via `libs.findVersion("android-compileSdk").get().toString().toInt()` (PITFALL #1); JVM 11 compile options.
- **`recipe.jvm.server.gradle.kts`** — Kotlin JVM + Ktor + Flyway + application; `jvmToolchain(21)` + `allWarningsAsErrors.set(true)`; 10 quoted `"implementation"(...)` deps (ktor-server*, logback, flyway-core + flyway-database-postgresql, postgresql JDBC, ktor-serverTestHost, kotlin-testJunit); `flyway{}` block with env-driven URL + `cleanDisabled = true`.
- **Root `settings.gradle.kts`** — added `includeBuild("build-logic")` as first statement inside existing `pluginManagement { }`.
- **Root `build.gradle.kts`** — appended `alias(libs.plugins.spotless) apply false` and `alias(libs.plugins.flywayPlugin) apply false`. Total apply-false count: 10.

## Commits

| Task | Description | Hash |
|------|-------------|------|
| 1 | Scaffold build-logic/ included build + 5 precompiled plugins | `6a69910` |
| 2 | Wire build-logic into root settings.gradle.kts + Spotless/Flyway apply-false | `60221f6` |

## Deviations from Plan

None — every `<automated>` grep block and acceptance criterion passed first-try.

## Note

This SUMMARY.md was drafted by the executor agent but hook-blocked from being written inside the worktree sandbox; the orchestrator persisted it after merging the worktree into master.

## Requirements completed

INFRA-02