---
phase: 01-project-infrastructure-module-wiring
plan: 07
type: execute
wave: 3
depends_on: [01, 02, 03, 04, 05, 06]
files_modified:
  - shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/.gitkeep
autonomous: true
requirements: [INFRA-01, INFRA-02, INFRA-03, INFRA-06]
requirements_addressed: [INFRA-01, INFRA-02, INFRA-03, INFRA-06]

must_haves:
  truths:
    - "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"
  artifacts:
    - path: "shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/.gitkeep"
      provides: "Empty package scaffold marker ensuring dev.ulfrx.recipe.shared package exists in git (Phase 2+ adds DTOs here)"
    - path: "composeApp/build/outputs/apk/debug/composeApp-debug.apk"
      provides: "Android debug APK artifact from ./gradlew build (SC1 proof)"
    - path: "composeApp/build/bin/iosSimulatorArm64/debugFramework/ComposeApp.framework"
      provides: "iOS framework artifact from ./gradlew build (SC1 proof)"
  key_links:
    - from: "./gradlew build"
      to: "composeApp/build.gradle.kts + shared/build.gradle.kts + server/build.gradle.kts"
      via: "recipe.* convention plugin application (Plan 03 refactor)"
      pattern: "id\\(\"recipe\\."
    - from: "./gradlew :composeApp:help"
      to: "build-logic/src/main/kotlin/recipe.kotlin.multiplatform.gradle.kts"
      via: "help task enumerates applied plugins"
      pattern: "recipe\\.kotlin\\.multiplatform"
---

<objective>
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`.
</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
@tools/verify-no-version-literals.sh
@tools/verify-shared-pure.sh
@tools/verify-ios-flags.sh
@CLAUDE.md

<interfaces>
<!-- Inputs from prior plans -->

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)
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create shared/ package scaffold placeholder</name>
  <files>shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared/.gitkeep</files>
  <read_first>
    - 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)
  </read_first>
  <action>
  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.
  </action>
  <verify>
    <automated>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</automated>
  </verify>
  <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>
  <done>Empty package scaffold created; shared/ is ready for Phase 2+ DTOs.</done>
</task>

<task type="auto">
  <name>Task 2: Run Spotless apply + full ./gradlew build + invariant scripts</name>
  <files></files>
  <read_first>
    - .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)
  </read_first>
  <action>
  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:

     ```bash
     ./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
     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
     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
     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:

     ```bash
     ./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:

     ```bash
     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:

     ```bash
     ./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):

     ```bash
     ./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.
  </action>
  <verify>
    <automated>./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</automated>
  </verify>
  <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>
  <done>Phase 1 green — all 5 SCs and all 4 phase requirements (INFRA-01/02/03/06) verified by automated commands.</done>
</task>

</tasks>

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

<verification>
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.
</verification>

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

<output>
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
</output>
