Wire project infrastructure
This commit is contained in:
@@ -0,0 +1,352 @@
|
||||
---
|
||||
phase: 01-project-infrastructure-module-wiring
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: [01, 02]
|
||||
files_modified:
|
||||
- composeApp/build.gradle.kts
|
||||
- shared/build.gradle.kts
|
||||
- server/build.gradle.kts
|
||||
- shared/src/jsMain
|
||||
autonomous: true
|
||||
requirements: [INFRA-02, INFRA-06]
|
||||
requirements_addressed: [INFRA-02, INFRA-06]
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "composeApp/build.gradle.kts applies recipe.kotlin.multiplatform + recipe.compose.multiplatform + recipe.android.application + recipe.quality, and nothing else (D-06 role declaration)"
|
||||
- "shared/build.gradle.kts applies recipe.kotlin.multiplatform + recipe.quality + androidLibrary alias, with explicitApi() set directly in the module (D-12)"
|
||||
- "server/build.gradle.kts applies recipe.jvm.server + recipe.quality, and keeps only the module-specific application { } block"
|
||||
- "The js target is removed from composeApp and shared (D-01); shared/src/jsMain/ directory is deleted"
|
||||
- "iosX64 target is never referenced (D-02) — only iosArm64 + iosSimulatorArm64 via the convention plugin"
|
||||
- "No version literals exist in any *.gradle.kts outside gradle/libs.versions.toml (INFRA-01 / D-09)"
|
||||
- "shared/ framework basename is overridden to 'Shared' (D-07, PITFALL #10); composeApp keeps 'ComposeApp' from the convention plugin"
|
||||
artifacts:
|
||||
- path: "composeApp/build.gradle.kts"
|
||||
provides: "Module build applying 4 recipe.* convention plugins + module-only source-set deps (androidMain, commonMain projects.shared, jvmMain desktop)"
|
||||
min_lines: 15
|
||||
- path: "shared/build.gradle.kts"
|
||||
provides: "Module build applying recipe.kotlin.multiplatform + recipe.quality + androidLibrary; enabling explicitApi(); overriding framework baseName to 'Shared'; keeping android { namespace } block"
|
||||
min_lines: 15
|
||||
- path: "server/build.gradle.kts"
|
||||
provides: "Module build applying recipe.jvm.server + recipe.quality; keeping application { mainClass } block and implementation(projects.shared) dep"
|
||||
min_lines: 10
|
||||
key_links:
|
||||
- from: "composeApp/build.gradle.kts"
|
||||
to: "build-logic/src/main/kotlin/recipe.kotlin.multiplatform.gradle.kts"
|
||||
via: "plugins { id(\"recipe.kotlin.multiplatform\") }"
|
||||
pattern: "id\\(\"recipe\\.kotlin\\.multiplatform\"\\)"
|
||||
- from: "composeApp/build.gradle.kts"
|
||||
to: "build-logic/src/main/kotlin/recipe.compose.multiplatform.gradle.kts"
|
||||
via: "plugins { id(\"recipe.compose.multiplatform\") }"
|
||||
pattern: "id\\(\"recipe\\.compose\\.multiplatform\"\\)"
|
||||
- from: "server/build.gradle.kts"
|
||||
to: "build-logic/src/main/kotlin/recipe.jvm.server.gradle.kts"
|
||||
via: "plugins { id(\"recipe.jvm.server\") }"
|
||||
pattern: "id\\(\"recipe\\.jvm\\.server\"\\)"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Refactor the three module build scripts (`composeApp/`, `shared/`, `server/`) to apply the convention plugins from Plan 02 and remove the content those plugins now own. Drop the `js` target (D-01), confirm `iosX64` stays absent (D-02), add `explicitApi()` + framework-basename override to `shared/` (D-12 / PITFALL #10), and ensure every module's `plugins { }` block reads as a role declaration (D-06). Also delete the `shared/src/jsMain/` source directory (D-01).
|
||||
|
||||
Purpose: This plan delivers INFRA-02's structural payoff — adding a new KMP module in the future should require only `plugins { id("recipe.kotlin.multiplatform") }` + source-set declarations, not copy-pasting Compose configs. It also delivers INFRA-06's structural prerequisite: after this refactor, `shared/` no longer pulls Compose transitively (because `recipe.compose.multiplatform` is applied only to `composeApp/`).
|
||||
|
||||
Output: Three rewritten `build.gradle.kts` files (each ≤40 lines), `shared/src/jsMain/` directory deleted. No `./gradlew build` run in this plan — Plan 04/05 verify via their own targets, Plan 07 runs the full green-build gate.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@$HOME/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/01-project-infrastructure-module-wiring/01-CONTEXT.md
|
||||
@.planning/phases/01-project-infrastructure-module-wiring/01-RESEARCH.md
|
||||
@.planning/phases/01-project-infrastructure-module-wiring/01-PATTERNS.md
|
||||
@.planning/phases/01-project-infrastructure-module-wiring/01-VALIDATION.md
|
||||
@composeApp/build.gradle.kts
|
||||
@shared/build.gradle.kts
|
||||
@server/build.gradle.kts
|
||||
@CLAUDE.md
|
||||
|
||||
<interfaces>
|
||||
<!-- Plans 01 + 02 must be complete before this plan runs. -->
|
||||
|
||||
From gradle/libs.versions.toml (Plan 01 extended):
|
||||
- `libs.plugins.androidLibrary` — still referenced as alias inside shared/build.gradle.kts
|
||||
- `libs.compose.uiToolingPreview` — referenced from composeApp/build.gradle.kts module-specific deps
|
||||
- `libs.androidx.activity.compose` — referenced from composeApp androidMain deps
|
||||
- `libs.kotlinx.coroutinesSwing` — referenced from composeApp jvmMain deps
|
||||
- `libs.compose.uiTooling` — referenced from composeApp debugImplementation
|
||||
- `libs.koin.android` — NEW alias (Plan 01) for MainApplication's `androidContext(...)` in Plan 04
|
||||
|
||||
From build-logic/src/main/kotlin/ (Plan 02 created):
|
||||
- `recipe.kotlin.multiplatform` — applies KMP, sets D-05 targets, JVM toolchain, adds koin-bom/koin-core/kermit to commonMain, kotlin-test to commonTest, allWarningsAsErrors
|
||||
- `recipe.compose.multiplatform` — applies Compose MP + hot-reload on top of KMP, adds compose-* deps to commonMain
|
||||
- `recipe.android.application` — applies com.android.application, sets namespace + SDK versions
|
||||
- `recipe.jvm.server` — applies kotlin(jvm) + io.ktor.plugin + flyway + all server deps + quoted-config dependency block
|
||||
- `recipe.quality` — applies Spotless + allWarningsAsErrors safety net
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Rewrite composeApp/build.gradle.kts and shared/build.gradle.kts, delete shared/src/jsMain/</name>
|
||||
<files>composeApp/build.gradle.kts, shared/build.gradle.kts, shared/src/jsMain</files>
|
||||
<read_first>
|
||||
- composeApp/build.gradle.kts (current 114 lines — target of rewrite)
|
||||
- shared/build.gradle.kts (current 55 lines — target of rewrite)
|
||||
- .planning/phases/01-project-infrastructure-module-wiring/01-PATTERNS.md lines 574-672 (exact deltas for composeApp + shared)
|
||||
- .planning/phases/01-project-infrastructure-module-wiring/01-CONTEXT.md D-01 (drop js), D-03 (no desktop packaging), D-12 (explicitApi on shared only), D-20 (namespace + baseName)
|
||||
- .planning/phases/01-project-infrastructure-module-wiring/01-RESEARCH.md lines 1144-1155 (Open Question #1 — keep com.android.library on shared/ in Phase 1)
|
||||
</read_first>
|
||||
<action>
|
||||
Two file rewrites plus one directory deletion.
|
||||
|
||||
**Rewrite 1: `composeApp/build.gradle.kts`** — replace entire file content with:
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
id("recipe.kotlin.multiplatform")
|
||||
id("recipe.compose.multiplatform")
|
||||
id("recipe.android.application")
|
||||
id("recipe.quality")
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
androidMain.dependencies {
|
||||
implementation(libs.compose.uiToolingPreview)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(libs.koin.android)
|
||||
}
|
||||
commonMain.dependencies {
|
||||
implementation(libs.compose.uiToolingPreview)
|
||||
implementation(projects.shared)
|
||||
}
|
||||
jvmMain.dependencies {
|
||||
implementation(compose.desktop.currentOs)
|
||||
implementation(libs.kotlinx.coroutinesSwing)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
debugImplementation(libs.compose.uiTooling)
|
||||
}
|
||||
```
|
||||
|
||||
DELETIONS relative to the current file:
|
||||
- DROP all 3 imports on lines 1-3 (no longer needed — convention plugins supply JvmTarget/ExperimentalWasmDsl/TargetFormat)
|
||||
- DROP the original `plugins { alias(...) alias(...) }` block (lines 5-11) — replaced with 4 recipe.* IDs
|
||||
- DROP the `kotlin { androidTarget { ... } iosArm64() iosSimulatorArm64() jvm { ... } js { ... } wasmJs { ... } }` structural block (lines 13-46) — moved into `recipe.kotlin.multiplatform`
|
||||
- DROP `commonMain.dependencies` Compose entries (lines 52-62 — compose.runtime, compose.foundation, compose.material3, compose.ui, compose.components.resources, androidx.lifecycle.viewmodelCompose, androidx.lifecycle.runtimeCompose) — moved into `recipe.compose.multiplatform`. KEEP `implementation(projects.shared)` and the module-only `implementation(libs.compose.uiToolingPreview)` (the preview tooling is needed by `@Preview` annotations in composeApp's common code).
|
||||
- DROP `commonTest.dependencies { implementation(libs.kotlin.test) }` (lines 63-65) — moved into `recipe.kotlin.multiplatform`
|
||||
- DROP the entire `android { ... }` block (lines 73-98) — moved into `recipe.android.application`
|
||||
- DROP `compose.desktop { application { ... nativeDistributions { ... } } }` (lines 104-114) — D-03 says no desktop packaging
|
||||
|
||||
ADDITIONS:
|
||||
- ADD `implementation(libs.koin.android)` to `androidMain.dependencies` (Plan 04's MainApplication.kt calls `androidContext(...)` which comes from koin-android; the catalog alias was added in Plan 01).
|
||||
|
||||
KEEP:
|
||||
- `androidMain.dependencies { implementation(libs.compose.uiToolingPreview); implementation(libs.androidx.activity.compose) }` — Android-only deps
|
||||
- `jvmMain.dependencies { implementation(compose.desktop.currentOs); implementation(libs.kotlinx.coroutinesSwing) }` — Desktop-only deps
|
||||
- `dependencies { debugImplementation(libs.compose.uiTooling) }` — Android debug-only tooling
|
||||
|
||||
**Rewrite 2: `shared/build.gradle.kts`** — replace entire file content with:
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
id("recipe.kotlin.multiplatform")
|
||||
id("recipe.quality")
|
||||
alias(libs.plugins.androidLibrary)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
explicitApi()
|
||||
|
||||
// Override framework baseName: shared exposes "Shared.framework" to Swift, while
|
||||
// composeApp's convention-plugin default is "ComposeApp.framework". (D-07 / PITFALL #10)
|
||||
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>().configureEach {
|
||||
binaries.withType<org.jetbrains.kotlin.gradle.plugin.mpp.Framework>().configureEach {
|
||||
baseName = "Shared"
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
// Phase 1: intentionally empty. Domain models + DTOs land Phase 2+.
|
||||
// D-19 / INFRA-06: Do NOT add Ktor, Compose, or SQLDelight deps here — EVER.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "dev.ulfrx.recipe.shared"
|
||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
DELETIONS relative to the current file:
|
||||
- DROP both imports on lines 1-2 (no longer needed)
|
||||
- DROP the original `plugins { alias(libs.plugins.kotlinMultiplatform); alias(libs.plugins.androidLibrary) }` (lines 4-7) — replaced with `id("recipe.kotlin.multiplatform")` + kept `alias(libs.plugins.androidLibrary)`
|
||||
- DROP the entire `kotlin { androidTarget { ... } iosArm64() iosSimulatorArm64() jvm { ... } js { ... } wasmJs { ... } ... }` structural block (lines 9-41) — moved into `recipe.kotlin.multiplatform`
|
||||
- DROP `js { browser() }` (lines 25-27) — D-01
|
||||
|
||||
ADDITIONS:
|
||||
- ADD `explicitApi()` inside the `kotlin { }` block (D-12 — strict on shared/ only, configured directly in module)
|
||||
- ADD the framework baseName override block targeting `KotlinNativeTarget`/`Framework` (overrides the convention plugin's `"ComposeApp"` default to `"Shared"` — D-07 / PITFALL #10)
|
||||
|
||||
KEEP:
|
||||
- `android { namespace = "dev.ulfrx.recipe.shared"; compileSdk; compileOptions; defaultConfig.minSdk }` — per 01-RESEARCH.md Open Question #1, keep `com.android.library` applied in Phase 1 (deferring the "do we need it" question to a future `recipe.android.library` plugin)
|
||||
|
||||
Note on `libs.versions.android.compileSdk.get().toInt()` vs `libs.findVersion(...)`: the `libs.versions.*` accessor IS available in MODULE `build.gradle.kts` files (it's only unavailable in precompiled plugins — PITFALL #1 applies only there). So the typed accessor is correct here.
|
||||
|
||||
**Directory deletion: `shared/src/jsMain/`**
|
||||
|
||||
Delete the entire `shared/src/jsMain/` directory (contains `kotlin/dev/ulfrx/recipe/Platform.js.kt`). D-01 drops the `js` target; with `recipe.kotlin.multiplatform` no longer declaring `js()`, this source directory becomes orphaned.
|
||||
|
||||
Run: `rm -rf shared/src/jsMain`
|
||||
|
||||
Do NOT delete `shared/src/wasmJsMain/` — `wasmJs` is kept per D-01. `composeApp/src/webMain/` is the wasmJs source set, also kept.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>grep -q 'id("recipe.kotlin.multiplatform")' composeApp/build.gradle.kts && grep -q 'id("recipe.compose.multiplatform")' composeApp/build.gradle.kts && grep -q 'id("recipe.android.application")' composeApp/build.gradle.kts && grep -q 'id("recipe.quality")' composeApp/build.gradle.kts && ! grep -q 'androidTarget' composeApp/build.gradle.kts && ! grep -q 'iosArm64' composeApp/build.gradle.kts && ! grep -q 'js {' composeApp/build.gradle.kts && ! grep -q 'nativeDistributions' composeApp/build.gradle.kts && ! grep -q '^android {' composeApp/build.gradle.kts && grep -q 'implementation(libs.koin.android)' composeApp/build.gradle.kts && grep -q 'id("recipe.kotlin.multiplatform")' shared/build.gradle.kts && grep -q 'id("recipe.quality")' shared/build.gradle.kts && grep -q 'explicitApi()' shared/build.gradle.kts && grep -q 'baseName = "Shared"' shared/build.gradle.kts && ! grep -q 'js {' shared/build.gradle.kts && ! test -d shared/src/jsMain && bash tools/verify-no-version-literals.sh && bash tools/verify-shared-pure.sh</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `composeApp/build.gradle.kts` has exactly 4 `id("recipe.*")` lines: `recipe.kotlin.multiplatform`, `recipe.compose.multiplatform`, `recipe.android.application`, `recipe.quality`
|
||||
- `composeApp/build.gradle.kts` does NOT contain `androidTarget`, `iosArm64`, `iosSimulatorArm64`, `jvm {`, `js {`, `wasmJs {`, or any `binaries.framework` block (all moved to convention plugin)
|
||||
- `composeApp/build.gradle.kts` does NOT contain an `^android {` block header (moved to `recipe.android.application`)
|
||||
- `composeApp/build.gradle.kts` does NOT contain `nativeDistributions` or `compose.desktop { application { ... } }` (D-03)
|
||||
- `composeApp/build.gradle.kts` does NOT contain `import org.jetbrains.compose.desktop.application.dsl.TargetFormat` (D-03)
|
||||
- `composeApp/build.gradle.kts` contains `implementation(libs.koin.android)` inside an `androidMain.dependencies` block
|
||||
- `composeApp/build.gradle.kts` contains `implementation(projects.shared)` in `commonMain.dependencies` (preserved for Plan 04 usage)
|
||||
- `composeApp/build.gradle.kts` line count ≤ 30 (was 114)
|
||||
- `shared/build.gradle.kts` has `id("recipe.kotlin.multiplatform")` + `id("recipe.quality")` + `alias(libs.plugins.androidLibrary)` (exactly 3 plugin applications)
|
||||
- `shared/build.gradle.kts` contains `explicitApi()` (D-12)
|
||||
- `shared/build.gradle.kts` contains `baseName = "Shared"` (exactly that capitalization — PITFALL #10)
|
||||
- `shared/build.gradle.kts` does NOT contain `js {` or `iosX64`
|
||||
- `shared/build.gradle.kts` contains the `android { namespace = "dev.ulfrx.recipe.shared" }` block (kept per Open Question #1)
|
||||
- `shared/src/jsMain` directory no longer exists (`test ! -d shared/src/jsMain`)
|
||||
- `tools/verify-no-version-literals.sh` exits 0 (no version literals leaked during rewrite)
|
||||
- `tools/verify-shared-pure.sh` exits 0 (shared/commonMain has only Greeting.kt/Platform.kt/Constants.kt — no forbidden imports)
|
||||
</acceptance_criteria>
|
||||
<done>Both module builds apply recipe.* conventions; js target source dir deleted; explicitApi + Shared basename set on shared/.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Rewrite server/build.gradle.kts</name>
|
||||
<files>server/build.gradle.kts</files>
|
||||
<read_first>
|
||||
- server/build.gradle.kts (current 23 lines — target of rewrite)
|
||||
- .planning/phases/01-project-infrastructure-module-wiring/01-PATTERNS.md lines 674-706 (server/build.gradle.kts delta)
|
||||
- .planning/phases/01-project-infrastructure-module-wiring/01-RESEARCH.md lines 556-605 (§ Pattern 7 — what's ALREADY in the convention plugin and does NOT need to be in the module)
|
||||
- .planning/phases/01-project-infrastructure-module-wiring/01-CONTEXT.md D-16 (server scope — Flyway, Postgres, /health)
|
||||
</read_first>
|
||||
<action>
|
||||
Replace the entire content of `server/build.gradle.kts` with:
|
||||
|
||||
```kotlin
|
||||
plugins {
|
||||
id("recipe.jvm.server")
|
||||
id("recipe.quality")
|
||||
}
|
||||
|
||||
group = "dev.ulfrx.recipe"
|
||||
version = "1.0.0"
|
||||
|
||||
application {
|
||||
mainClass.set("dev.ulfrx.recipe.ApplicationKt")
|
||||
|
||||
val isDevelopment: Boolean = project.ext.has("development")
|
||||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.shared)
|
||||
}
|
||||
```
|
||||
|
||||
DELETIONS:
|
||||
- DROP original plugins block (lines 1-5 — `alias(libs.plugins.kotlinJvm); alias(libs.plugins.ktor); application`) → replaced with 2 recipe.* IDs. The `application` plugin is applied by `recipe.jvm.server`.
|
||||
- DROP individual dependency lines (lines 16-22 — `libs.logback`, `libs.ktor.serverCore`, `libs.ktor.serverNetty`, `libs.ktor.serverTestHost`, `libs.kotlin.testJunit`) → all moved into `recipe.jvm.server`.
|
||||
|
||||
KEEP:
|
||||
- `group = "dev.ulfrx.recipe"` and `version = "1.0.0"` (module coordinates — per-module concern)
|
||||
- `application { mainClass.set(...) }` + `applicationDefaultJvmArgs` (per-module config — the `application` plugin is applied by `recipe.jvm.server` but this config is module-specific)
|
||||
- `implementation(projects.shared)` — module-specific project dependency (server depends on shared for Greeting, SERVER_PORT, future DTOs)
|
||||
|
||||
Note: `ktor-serverContentNegotiation`, `ktor-serializationKotlinxJson`, `flyway-core`, `flyway-database-postgresql`, `postgresql` are ALL bundled in `recipe.jvm.server` and do NOT need to be declared here.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>grep -q 'id("recipe.jvm.server")' server/build.gradle.kts && grep -q 'id("recipe.quality")' server/build.gradle.kts && ! grep -q 'libs.plugins.kotlinJvm' server/build.gradle.kts && ! grep -q 'libs.plugins.ktor' server/build.gradle.kts && grep -q 'mainClass.set("dev.ulfrx.recipe.ApplicationKt")' server/build.gradle.kts && grep -q 'implementation(projects.shared)' server/build.gradle.kts && ! grep -q 'libs.logback' server/build.gradle.kts && ! grep -q 'libs.ktor.serverCore' server/build.gradle.kts && bash tools/verify-no-version-literals.sh</automated>
|
||||
</verify>
|
||||
<acceptance_criteria>
|
||||
- `server/build.gradle.kts` has exactly 2 `id("recipe.*")` lines: `recipe.jvm.server`, `recipe.quality`
|
||||
- `server/build.gradle.kts` does NOT contain `alias(libs.plugins.kotlinJvm)` or `alias(libs.plugins.ktor)`
|
||||
- `server/build.gradle.kts` does NOT contain `libs.logback`, `libs.ktor.serverCore`, `libs.ktor.serverNetty`, `libs.ktor.serverTestHost`, or `libs.kotlin.testJunit` (all relocated to convention plugin)
|
||||
- `server/build.gradle.kts` contains `mainClass.set("dev.ulfrx.recipe.ApplicationKt")` (unchanged)
|
||||
- `server/build.gradle.kts` contains `implementation(projects.shared)` (unchanged)
|
||||
- `server/build.gradle.kts` contains `group = "dev.ulfrx.recipe"` and `version = "1.0.0"` (unchanged module coordinates)
|
||||
- `server/build.gradle.kts` line count ≤ 20 (was 23; effectively unchanged but deps block shrinks)
|
||||
- `tools/verify-no-version-literals.sh` exits 0
|
||||
</acceptance_criteria>
|
||||
<done>server build applies recipe.jvm.server + recipe.quality; module-only config (mainClass, shared dep) preserved.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<threat_model>
|
||||
## Trust Boundaries
|
||||
|
||||
| Boundary | Description |
|
||||
|----------|-------------|
|
||||
| Module build scripts → build-logic precompiled plugins | Same repo; plugins apply privileged build configuration (namespace, SDK versions, dep injection). No external trust boundary. |
|
||||
| Gradle module configuration → dependency resolution | Same as Plan 02 — aliases resolved via `libs.versions.toml` (pinned); no runtime consequences until Plan 04/05 actually compile code. |
|
||||
|
||||
## STRIDE Threat Register
|
||||
|
||||
| Threat ID | Category | Component | Disposition | Mitigation Plan |
|
||||
|-----------|----------|-----------|-------------|-----------------|
|
||||
| T-01-03-01 | Tampering (supply-chain leak) | Accidental version literal in rewrites | mitigate | Every task's `<automated>` runs `tools/verify-no-version-literals.sh` which scans every `*.gradle.kts` after the rewrite. Any inlined version (e.g. a forgotten `"1.0.0"` as a dep version) fails the check. Note: `version = "1.0.0"` on `server/build.gradle.kts` line 2 is PROJECT coordinate, not a dependency version — the verify script targets `version\s*=\s*"[0-9]` inside dependency declarations only; project-version assignments pass (not declared as `libs.*` lookup). Verify script scope matches PATTERNS.md spec. |
|
||||
| T-01-03-02 | Elevation of Privilege | Compose deps leak into shared/ | mitigate | `shared/build.gradle.kts` applies ONLY `recipe.kotlin.multiplatform` + `recipe.quality` + `androidLibrary` — NOT `recipe.compose.multiplatform`. Plan 07's `tools/verify-shared-pure.sh` will catch forbidden imports if they ever appear. |
|
||||
| T-01-03-03 | Denial of Service | Missing `recipe.compose.multiplatform` application on composeApp breaks Compose | mitigate | Task 1 `<acceptance_criteria>` greps for all 4 recipe IDs explicitly. Plan 04 will fail at compile time if the Compose plugin ID is missing. |
|
||||
| T-01-03-04 | Tampering | `js` target remnants in source tree after D-01 drop | mitigate | Task 1 explicitly deletes `shared/src/jsMain/` directory and greps for `js {` blocks. `composeApp/src/webMain/` (wasmJs target, kept) is NOT touched. |
|
||||
</threat_model>
|
||||
|
||||
<verification>
|
||||
Phase-level verification for this plan:
|
||||
|
||||
- All three `tools/verify-*.sh` scripts exit 0 after rewrites.
|
||||
- `shared/src/jsMain/` directory no longer exists.
|
||||
- `composeApp/build.gradle.kts` shrinks from 114 to ~30 lines — INFRA-02 payoff visible.
|
||||
- `shared/build.gradle.kts` shrinks from 55 to ~35 lines and now sets `explicitApi()`.
|
||||
|
||||
Optional sanity check (NOT in `<automated>` — Plan 07 runs the full gate):
|
||||
- `./gradlew :composeApp:help -q` emits a non-empty help output without a configuration error (proves plugin IDs resolve). Skip for speed — Plan 04 and Plan 05 will surface plugin-application errors via their own `./gradlew` targets.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- `composeApp/build.gradle.kts` applies 4 recipe.* IDs and contains NO `kotlin { androidTarget { ... } ... }` structural block and NO `android { ... }` block and NO `nativeDistributions`
|
||||
- `shared/build.gradle.kts` applies 3 plugins (2 recipe.* + androidLibrary), enables `explicitApi()`, overrides baseName to `"Shared"`
|
||||
- `server/build.gradle.kts` applies 2 recipe.* IDs and keeps only `application { mainClass }` + `implementation(projects.shared)`
|
||||
- `shared/src/jsMain/` deleted
|
||||
- `tools/verify-no-version-literals.sh` exits 0
|
||||
- `tools/verify-shared-pure.sh` exits 0
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/01-project-infrastructure-module-wiring/01-03-SUMMARY.md` recording: final LOC of each module build file (target: composeApp ≤30, shared ≤35, server ≤20), any deviations from the canonical patterns (expected: none), and confirmation that `shared/src/jsMain/` is gone.
|
||||
</output>
|
||||
Reference in New Issue
Block a user