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

18 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, requirements_addressed, must_haves
phase plan type wave depends_on files_modified autonomous requirements requirements_addressed must_haves
01-project-infrastructure-module-wiring 07 execute 3
01
02
03
04
05
06
shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/.gitkeep
true
INFRA-01
INFRA-02
INFRA-03
INFRA-06
INFRA-01
INFRA-02
INFRA-03
INFRA-06
truths artifacts key_links
shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/ package scaffold exists (as .gitkeep marker) — INFRA-06 file-existence criterion
./gradlew spotlessApply runs green (no files need formatting, OR all files are auto-formatted)
./gradlew build succeeds across composeApp, server, shared — produces Android APK + iOS framework + server JAR (SC1)
tools/verify-no-version-literals.sh exits 0 across the whole repo (SC2 / INFRA-01)
tools/verify-ios-flags.sh exits 0 (SC3 / INFRA-03)
tools/verify-shared-pure.sh exits 0 (SC5 / INFRA-06)
./gradlew :composeApp:help emits 'recipe.kotlin.multiplatform' among applied plugins (SC4 / INFRA-02)
./gradlew check runs spotlessCheck + all tests and exits 0
path provides
shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/.gitkeep Empty package scaffold marker ensuring dev.ulfrx.recipe.shared package exists in git (Phase 2+ adds DTOs here)
path provides
composeApp/build/outputs/apk/debug/composeApp-debug.apk Android debug APK artifact from ./gradlew build (SC1 proof)
path provides
composeApp/build/bin/iosSimulatorArm64/debugFramework/ComposeApp.framework iOS framework artifact from ./gradlew build (SC1 proof)
from to via pattern
./gradlew build composeApp/build.gradle.kts + shared/build.gradle.kts + server/build.gradle.kts recipe.* convention plugin application (Plan 03 refactor) id("recipe.
from to via pattern
./gradlew :composeApp:help build-logic/src/main/kotlin/recipe.kotlin.multiplatform.gradle.kts help task enumerates applied plugins recipe.kotlin.multiplatform
Create the final piece of INFRA-06 (empty `dev.ulfrx.recipe.shared` package scaffold under `shared/src/commonMain`) and then run the full phase verification gate: `./gradlew spotlessApply`, `./gradlew build`, the 3 `tools/verify-*.sh` invariant scripts, and `./gradlew check`. This is the "green build" moment that every prior plan in Phase 1 has been building toward.

Purpose: Phase 1 success is defined by 5 ROADMAP success criteria (SC1-SC5) and 4 phase requirements (INFRA-01/02/03/06). Plans 01-06 delivered the files and refactors; this plan PROVES they integrate cleanly. Any regression here is a phase-completion blocker.

Output: 1 .gitkeep placeholder + verification artifacts (APK + iOS framework) + proof of all 5 SCs + green ./gradlew check.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_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 @tools/verify-no-version-literals.sh @tools/verify-shared-pure.sh @tools/verify-ios-flags.sh @CLAUDE.md

From Plan 01:

  • tools/verify-no-version-literals.sh — greps every *.gradle.kts for version literals (exits 0 if none except build-logic/build.gradle.kts)
  • tools/verify-shared-pure.sh — greps shared/src/commonMain/ for forbidden imports (exits 0 if none OR if directory absent)
  • tools/verify-ios-flags.sh — greps gradle.properties for the two iOS K/N flags (exits 0 if both present)

From Plan 02:

  • build-logic/ with 5 precompiled plugins applied via settings.gradle.kts pluginManagement.includeBuild

From Plan 03:

  • composeApp/, shared/, server/ build.gradle.kts applying recipe.* convention plugins

From Plan 04:

  • composeApp common/iOS/Android/Desktop/Wasm entry points calling initKoin() + configureLogging()
  • iosApp/iosApp/iOSApp.swift calling KoinIosKt.doInitKoin()

From Plan 05:

  • server Application.kt with /health + Database.migrate + ContentNegotiation + extracted configureRouting()
  • server ApplicationTest.kt passing without Postgres

From Plan 06:

  • docker-compose.yml with postgres:16 + matching credentials
  • README.md with Local development section

Phase gate commands (from 01-VALIDATION.md § Sampling Rate):

  • Quick: ./gradlew spotlessCheck :server:test :shared:jvmTest (<30s)
  • Per-wave: ./gradlew build (full — iOS framework link + Android APK + server JAR)
  • Phase gate: ./gradlew check + manual curl + iOS simulator boot (simulator boot is a manual-only verification, 01-VALIDATION.md § Manual-Only)
Task 1: Create shared/ package scaffold placeholder shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/.gitkeep - shared/src/commonMain/kotlin/dev/ulfrx/recipe/ (current contents: Greeting.kt, Platform.kt, Constants.kt — these are the TEMPLATE classes; they stay in place for now. Phase 2+ reorganizes.) - .planning/phases/01-project-infrastructure-module-wiring/01-CONTEXT.md D-19 (shared/commonMain stays pure; Phase 1 ships an empty package scaffold under dev.ulfrx.recipe.shared) - .planning/phases/01-project-infrastructure-module-wiring/01-PATTERNS.md lines 73-77 (shared package scaffold as .gitkeep marker) - .planning/phases/01-project-infrastructure-module-wiring/01-RESEARCH.md line 289 (shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/ NEW empty pkg) Create an empty `.gitkeep` file at `shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/.gitkeep`. The parent directories do not exist yet — create them as part of the write.

The file content is zero bytes (empty). Its purpose is purely to make dev.ulfrx.recipe.shared package discoverable in git and in the IDE, ready for Phase 2+ DTO additions.

DO NOT:

  • Touch or delete shared/src/commonMain/kotlin/dev/ulfrx/recipe/Greeting.kt — template class, stays
  • Touch Platform.kt or Constants.kt — template classes, stay
  • Add any other file under the new shared/ package
  • Add expect/actual declarations anywhere in shared/ (Phase 2+ scope)

Note the namespace layering: shared/src/commonMain/kotlin/dev/ulfrx/recipe/ is the ROOT package (dev.ulfrx.recipe — where Constants.kt lives), and shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/ is a SUB-package (dev.ulfrx.recipe.shared — where Phase 2+ DTOs will live). Both are valid; Phase 1 keeps the root-package template files and adds the sub-package placeholder. test -f shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/.gitkeep && test -d shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared && test -f shared/src/commonMain/kotlin/dev/ulfrx/recipe/Greeting.kt && test -f shared/src/commonMain/kotlin/dev/ulfrx/recipe/Constants.kt && bash tools/verify-shared-pure.sh <acceptance_criteria> - shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/.gitkeep exists (file test: test -f) - Parent directory shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared exists (directory test: test -d) - Existing template files are preserved: shared/src/commonMain/kotlin/dev/ulfrx/recipe/Greeting.kt, Platform.kt, Constants.kt all still exist - tools/verify-shared-pure.sh exits 0 — the .gitkeep file is not a .kt file so the grep skips it; the existing Greeting/Platform/Constants files still contain no forbidden imports </acceptance_criteria> Empty package scaffold created; shared/ is ready for Phase 2+ DTOs.

Task 2: Run Spotless apply + full ./gradlew build + invariant scripts - .planning/phases/01-project-infrastructure-module-wiring/01-VALIDATION.md lines 40-58 (Per-Task Verification Map — the exact commands this task runs) - .planning/phases/01-project-infrastructure-module-wiring/01-VALIDATION.md lines 27-34 (Sampling Rate — per-wave and phase-gate commands) - .planning/phases/01-project-infrastructure-module-wiring/01-RESEARCH.md lines 1216-1241 (Success Criteria → Test Map) This task is purely verification — no file modifications. Run the full phase gate in sequence. If any step fails, STOP and report the failure (do NOT silently swallow errors — a failure here means a prior plan regressed and must be fixed before Phase 1 completes).

Execute these commands IN ORDER. Each must exit 0 before proceeding to the next.

  1. Spotless apply — auto-formats Kotlin + Gradle + Markdown files across all modules using recipe.quality's ktlint rules:

    ./gradlew spotlessApply
    

    Expected: exit 0. If formatting changes any file, the change is benign (whitespace/indentation normalization); the subsequent build still passes.

  2. Invariant script: no version literals — enforces INFRA-01 SC#2:

    bash tools/verify-no-version-literals.sh
    

    Expected: exit 0 + OK: no version literals outside catalog.

  3. Invariant script: shared/ is pure — enforces INFRA-06 SC#5:

    bash tools/verify-shared-pure.sh
    

    Expected: exit 0 + OK: shared/commonMain is pure.

  4. Invariant script: iOS K/N flags present — enforces INFRA-03 SC#3:

    bash tools/verify-ios-flags.sh
    

    Expected: exit 0 + OK: iOS binary flags present.

  5. Full Gradle build — enforces SC1: produces Android APK + iOS framework + server JAR:

    ./gradlew build
    

    Expected: exit 0. This compiles every target (androidTarget, iosArm64, iosSimulatorArm64, jvm, wasmJs), links the iOS framework, packages the Android APK, and builds the server fat JAR.

    After success, verify the two proof artifacts exist:

    test -f composeApp/build/outputs/apk/debug/composeApp-debug.apk
    test -d composeApp/build/bin/iosSimulatorArm64/debugFramework/ComposeApp.framework
    
  6. Convention plugin applied — enforces SC4 / INFRA-02:

    ./gradlew :composeApp:help -q 2>&1 | grep -q 'recipe.kotlin.multiplatform' || ./gradlew :composeApp:tasks --all -q 2>&1 | grep -q 'recipe' || true
    

    Ktlint/help output verification: the help task for a module does not always enumerate plugins in recent Gradle versions. An alternative proof: the ./gradlew build success in step 5 IS the proof that recipe.kotlin.multiplatform was applied — if the plugin hadn't applied, compilation would have failed at configuration time. Record the ./gradlew build success as SC4 satisfaction if help output is ambiguous.

  7. Full check — enforces full-suite green (spotlessCheck + all tests):

    ./gradlew check
    

    Expected: exit 0. This includes:

    • spotlessCheck (Spotless verification)
    • :server:test (runs the /health test from Plan 05 — no Postgres needed)
    • :composeApp:jvmTest (template test, if present)
    • :shared:jvmTest (template test, if present)
    • Other platform tests as declared

If any of steps 1-7 fails, report exactly which step failed, the full error output, and STOP. The failure indicates a regression in one of Plans 01-06 that needs a /gsd-plan-phase --gaps cycle.

IMPORTANT:

  • Do NOT add a docker compose up postgres step here. The /health test in Plan 05 composes configureRouting() directly WITHOUT Database.migrate() — no Postgres required. The only manual-only verification in Phase 1 is iOS simulator boot (01-VALIDATION.md § Manual-Only) which is deferred to a later human review.
  • Do NOT run ./gradlew :server:run here — it would call Database.migrate() which requires a running Postgres. That's a manual smoke check (documented in README Local development) not a CI/phase-gate check. ./gradlew spotlessApply -q && bash tools/verify-no-version-literals.sh && bash tools/verify-shared-pure.sh && bash tools/verify-ios-flags.sh && ./gradlew build -q && test -f composeApp/build/outputs/apk/debug/composeApp-debug.apk && test -d composeApp/build/bin/iosSimulatorArm64/debugFramework/ComposeApp.framework && ./gradlew check -q <acceptance_criteria>
    • ./gradlew spotlessApply exits 0
    • tools/verify-no-version-literals.sh exits 0 (SC2)
    • tools/verify-shared-pure.sh exits 0 (SC5)
    • tools/verify-ios-flags.sh exits 0 (SC3)
    • ./gradlew build exits 0 (SC1)
    • composeApp/build/outputs/apk/debug/composeApp-debug.apk exists (SC1 Android artifact)
    • composeApp/build/bin/iosSimulatorArm64/debugFramework/ComposeApp.framework directory exists (SC1 iOS artifact)
    • ./gradlew check exits 0 (full-suite verification — includes spotlessCheck + all tests including /health)
    • The ./gradlew build success implicitly proves SC4 (convention plugins applied) — if recipe.kotlin.multiplatform hadn't applied, the build would have failed during module configuration
    • No BUILD FAILED string appears in the transcript </acceptance_criteria> Phase 1 green — all 5 SCs and all 4 phase requirements (INFRA-01/02/03/06) verified by automated commands.

<threat_model>

Trust Boundaries

Boundary Description
Developer host → Gradle daemon Same process; Gradle executes precompiled plugin code from build-logic/ with full project access by design.
Gradle build → Maven Central + Gradle Plugin Portal + Google First ./gradlew build downloads new artifacts (Koin, Kermit, Spotless, Flyway, Postgres JDBC, ktor content-negotiation, kotlinx-serialization). All versions pinned via catalog (Plan 01).
iOS framework link → K/N compiler Uses the two binary flags from gradle.properties (gc=cms, objcDisposeOnMain=false). Verified by tools/verify-ios-flags.sh (infrastructure check) + deferred iOS simulator boot check (manual).

STRIDE Threat Register

Threat ID Category Component Disposition Mitigation Plan
T-01-07-01 Denial of Service ./gradlew build downloading fresh deps, causing slow first-build accept First build may take 2-5 minutes as Koin/Kermit/Flyway/Postgres JDBC artifacts download (~80 MB per 01-RESEARCH.md § Runtime State Inventory). Subsequent builds use Gradle cache. Not a threat — just an expectation.
T-01-07-02 Tampering (supply chain) Malicious transitive dep snuck in via new library mitigate Every new dep is pinned via catalog (Plan 01). Gradle verification metadata (gradle/verification-metadata.xml) is NOT enabled in Phase 1 — it's a future enhancement (Phase 11 CI setup). Risk accepted for Phase 1 single-dev local-build scope.
T-01-07-03 Destruction Stale build/ cache from template's js target outputs mitigate 01-RESEARCH.md § Runtime State Inventory notes developers should ./gradlew clean once after Phase 1 to flush stale js target outputs. Task 2's ./gradlew build will still succeed (Gradle ignores orphaned outputs), but developers may see bloated build/ until a clean. README Local development section's ./gradlew check implicitly clears enough; full clean is a nice-to-have.
T-01-07-04 Information Disclosure ./gradlew build log leaking env variables to console accept Server-side env vars (DATABASE_URL etc.) are only read at server boot, not during ./gradlew build. The /health test composes routing without the DB. No secrets logged during build.
</threat_model>
Phase-level verification for this plan — this IS the phase gate. Success here equals Phase 1 completion.

Hard gate commands (all must exit 0):

  1. ./gradlew spotlessApply — auto-format
  2. tools/verify-no-version-literals.sh — SC2 / INFRA-01
  3. tools/verify-shared-pure.sh — SC5 / INFRA-06
  4. tools/verify-ios-flags.sh — SC3 / INFRA-03
  5. ./gradlew build — SC1, implicitly SC4 / INFRA-02
  6. ./gradlew check — full-suite (spotlessCheck + all tests)

Manual-only verifications (deferred per 01-VALIDATION.md § Manual-Only — NOT in Task 2 <automated>):

  • iOS simulator debug launch without legacy memory-manager warnings (requires Xcode + simulator)
  • Hot-reload dev loop on Desktop (interactive)
  • Server /health reachable via curl when Postgres is up (requires docker compose up -d postgres + ./gradlew :server:run)

These manual checks are recommended for the developer to run once; they are NOT gate-blocking for automated Phase 1 completion.

<success_criteria>

  • shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/.gitkeep created
  • ./gradlew spotlessApply green
  • All 3 tools/verify-*.sh scripts green
  • ./gradlew build green + Android APK + iOS framework artifacts exist
  • ./gradlew check green
  • No manual step required to pass this plan </success_criteria>
After completion, create `.planning/phases/01-project-infrastructure-module-wiring/01-07-SUMMARY.md` recording: the final 7 verification command outputs (exit codes), the size of the produced APK and iOS framework, the total `./gradlew build` time, and explicit confirmation that all 5 ROADMAP SCs (SC1-SC5) and 4 phase requirements (INFRA-01/02/03/06) are satisfied.

Include in the summary a brief "Manual smoke checks to run later" list pointing at 01-VALIDATION.md § Manual-Only:

  • iOS simulator boot without legacy-MM warnings
  • Desktop hot-reload regression check
  • docker compose up postgres + server /health curl smoke test