298 lines
18 KiB
Markdown
298 lines
18 KiB
Markdown
---
|
|
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>cd /Users/rwilk/dev/repo/recipe && ./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>
|