7.9 KiB
phase, slug, status, nyquist_compliant, wave_0_complete, created
| phase | slug | status | nyquist_compliant | wave_0_complete | created |
|---|---|---|---|---|---|
| 1 | project-infrastructure-module-wiring | draft | false | false | 2026-04-24 |
Phase 1 — Validation Strategy
Per-phase validation contract derived from
01-RESEARCH.md § Validation Architecture. Phase 1 is predominantly build-level verification (Gradle tasks, file structure, grep invariants) rather than unit tests. The existingApplicationTest.ktis the one test file extended (adds/healthcoverage).
Test Infrastructure
| Property | Value |
|---|---|
| Framework | kotlin.test (commonTest) + ktor-server-test-host (JUnit 4 runner for server) + existing KMP template test stubs |
| Config file | composeApp/src/commonTest/kotlin/ComposeAppCommonTest.kt, shared/src/commonTest/kotlin/SharedCommonTest.kt, server/src/test/kotlin/dev/ulfrx/recipe/ApplicationTest.kt (all present from template) |
| Quick run command | ./gradlew :server:test :composeApp:jvmTest :shared:jvmTest (JVM-only, <30s) |
| Full suite command | ./gradlew check (runs spotlessCheck + every *Test task across all targets) |
| Estimated runtime | ~30s quick / ~3–5 min full (cold) |
Sampling Rate
- After every task commit:
./gradlew spotlessCheck :server:test :shared:jvmTest(fast subset, <30s) - After every plan wave:
./gradlew build(includes iOS framework link + Android APK) - Before
/gsd-verify-work(phase gate):./gradlew check+ manual server/healthcurl + iOS simulator boot check - Max feedback latency: 30s (quick subset) / 5 min (full)
Per-Task Verification Map
Note: Task IDs are populated by gsd-planner when PLAN.md files are written. Each row below is the per-requirement contract the planner MUST map to at least one task's <automated> block. Rows marked "Wave 0" require a helper file to be created before task execution can verify it.
| Behavior | Requirement | Test Type | Automated Command | File Exists | Status |
|---|---|---|---|---|---|
No version literals in any build.gradle.kts |
INFRA-01 | shell grep | tools/verify-no-version-literals.sh |
❌ Wave 0 | ⬜ pending |
gradle/libs.versions.toml is the single source of truth |
INFRA-01 | grep | grep -rE "libs\\.(versions|plugins|bundles)" build-logic/src/main/kotlin/ returns all version lookups |
✅ catalog exists | ⬜ pending |
| Convention plugins apply without duplication | INFRA-02 | Gradle | ./gradlew :composeApp:help :server:help :shared:help shows recipe.* in applied plugins |
❌ Wave 0 (plugins don't exist yet) | ⬜ pending |
Adding a new KMP module only needs id("recipe.kotlin.multiplatform") |
INFRA-02 | visual | refactored shared/build.gradle.kts ≤15 LOC |
Target Wave 2 | ⬜ pending |
gradle.properties contains both iOS K/N flags |
INFRA-03 | grep | tools/verify-ios-flags.sh |
❌ Wave 0 | ⬜ pending |
| iOS simulator build has no legacy memory-manager warnings | INFRA-03 | build-log | ./gradlew :composeApp:linkDebugFrameworkIosSimulatorArm64 --info 2>&1 | grep -iE 'legacy|freeze|SharedImmutable' is empty |
Wave 2 (iOS) | ⬜ pending |
shared/commonMain has no Ktor/Compose/SQLDelight imports |
INFRA-06 | grep | tools/verify-shared-pure.sh |
❌ Wave 0 | ⬜ pending |
shared/ package scaffold exists |
INFRA-06 | file | test -d shared/src/commonMain/kotlin/dev/ulfrx/recipe/shared |
Wave 2 | ⬜ pending |
SC1: ./gradlew build succeeds + produces iOS framework + APK |
ROADMAP SC1 | Gradle | ./gradlew build && test -f composeApp/build/outputs/apk/debug/composeApp-debug.apk && test -d composeApp/build/bin/iosSimulatorArm64/debugFramework/ComposeApp.framework |
Phase gate | ⬜ pending |
SC4: each module's help shows its convention plugins |
ROADMAP SC4 | Gradle | ./gradlew :composeApp:help -q | grep 'recipe.kotlin.multiplatform' etc. |
Phase gate | ⬜ pending |
Server /health returns 200 JSON {"status":"ok"} |
D-16 | integration | ./gradlew :server:test --tests "*HealthRoute*" (added to ApplicationTest.kt) |
❌ Wave 0 (test update) | ⬜ pending |
| Server fails loudly if Postgres unreachable | D-16 | manual | docker compose down; ./gradlew :server:run exits non-zero with "Database unreachable" in logs |
Phase gate | ⬜ pending |
| Spotless formatting clean | D-10 | Gradle | ./gradlew spotlessCheck |
Per-commit | ⬜ pending |
| Koin starts without double-init | D-14 | Gradle test | ./gradlew :composeApp:jvmTest (template test exercises App() composition path; no KoinApplicationAlreadyStartedException) |
Per-wave | ⬜ pending |
Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky
Wave 0 Requirements
These assets MUST exist before any verification task can run green. The planner should place them in Wave 0 (or inside the plan that creates the infrastructure they verify).
tools/verify-no-version-literals.sh— greps everybuild.gradle.kts+build-logic/**/*.gradle.ktsfor a non-test numeric version literal; exits non-zero on matchtools/verify-shared-pure.sh— grepsshared/src/commonMain/for forbidden imports (io.ktor,androidx.compose,org.jetbrains.compose,app.cash.sqldelight); exits non-zero on matchtools/verify-ios-flags.sh— grepsgradle.propertiesforkotlin.native.binary.objcDisposeOnMain=falseANDkotlin.native.binary.gc=cms; exits non-zero if either is missingbuild-logic/scaffold —settings.gradle.kts,build.gradle.kts, and 5src/main/kotlin/recipe.*.gradle.ktsstubsserver/src/main/resources/application.conf— HOCON withktor.deployment,database.url/user/passwordusing${?X}env overridesserver/src/main/resources/db/migration/.gitkeep— directory placeholder for Flywaydocker-compose.yml—postgres:16service with named volume + healthcheckserver/src/test/kotlin/dev/ulfrx/recipe/ApplicationTest.kt— extended with/healthendpoint assertioncomposeApp/src/commonMain/kotlin/dev/ulfrx/recipe/di/Koin.kt+AppModule.kt—initKoin()helper + empty modulecomposeApp/src/commonMain/kotlin/dev/ulfrx/recipe/logging/Logging.kt— KermitsetTag("recipe")composeApp/src/androidMain/kotlin/dev/ulfrx/recipe/MainApplication.kt+AndroidManifest.xmlregistration — callsinitKoin { androidContext(this) }composeApp/src/iosMain/kotlin/dev/ulfrx/recipe/di/KoinIos.kt—fun doInitKoin()exported for SwiftiosApp/iosApp/iOSApp.swift— modified to callKoinIosKt.doInitKoin()ininit()
Manual-Only Verifications
| Behavior | Requirement | Why Manual | Test Instructions |
|---|---|---|---|
| iOS simulator debug launch has no legacy K/N memory-manager warnings | INFRA-03 / SC3 | Requires Xcode simulator boot; not scriptable from Gradle reliably on CI | Run ./gradlew :composeApp:iosSimulatorArm64Test OR open iosApp.xcworkspace in Xcode, run on iPhone 15 simulator, inspect console for legacy/freeze/SharedImmutable — expect none |
Hot-reload dev loop on Desktop still works post-refactor (regression check for commit c50d747) |
— | Interactive | ./gradlew :composeApp:jvmRun --mainClass MainKt --auto-reload; edit App.kt, observe reload without rebuild |
Server /health reachable via curl when Postgres up |
D-16 | Requires running Postgres + server process | docker compose up -d postgres, ./gradlew :server:run &, sleep 5, curl -sf http://localhost:8080/health returns {"status":"ok"} |
Validation Sign-Off
- All tasks have
<automated>verify or Wave 0 dependencies - Sampling continuity: no 3 consecutive tasks without automated verify
- Wave 0 covers all MISSING references (13 items listed above)
- No watch-mode flags in any verification command
- Feedback latency < 30s (quick) / 5min (full)
nyquist_compliant: trueset in frontmatter after planner maps every task to a row above
Approval: pending