Files
recipe/.planning/phases/02.1-app-shell-navigation-search-foundation/02.1-VALIDATION.md

7.5 KiB

phase, slug, status, nyquist_compliant, wave_0_complete, created
phase slug status nyquist_compliant wave_0_complete created
2.1 app-shell-navigation-search-foundation draft false false 2026-05-08

Phase 2.1 — Validation Strategy

Per-phase validation contract for feedback sampling during execution. Sourced from 02.1-RESEARCH.md § Validation Architecture.


Test Infrastructure

Property Value
Framework kotlin.test (commonTest) — already used in Phase 2 (AuthSessionTest, LoginViewModelTest)
Config file none — convention plugins handle recipe.kotlin.multiplatform
Quick run command ./gradlew :composeApp:commonTest --tests "dev.ulfrx.recipe.ui.screens.shell.*" --tests "dev.ulfrx.recipe.ui.screens.recipes.*Search*" --tests "dev.ulfrx.recipe.navigation.*" --tests "dev.ulfrx.recipe.ui.components.glass.*"
Full suite command ./gradlew :composeApp:check
Estimated runtime ~30-90 seconds (commonTest); ~3-6 min (full check incl. iOS sim klib link)

Compose UI Test on KMP iOS is not introduced this phase — feasibility is too low. Visible chrome is verified by a manual smoke runbook (see § Manual-Only Verifications).


Sampling Rate

  • After every task commit: Run ./gradlew :composeApp:commonTest
  • After every plan wave: Run ./gradlew :composeApp:check
  • Before /gsd-verify-work: Full suite green AND manual iOS-simulator smoke runbook executed
  • Max feedback latency: ~90 seconds (commonTest)

Per-Task Verification Map

Task IDs are filled in by the planner. The rows below are the requirement-level verification anchors that any plan task must map onto via its verify block.

Anchor Plan Wave Requirement Threat Ref Secure Behavior Test Type Automated Command File Exists Status
V-01 TBD 1 UI-03 navigateToTab() applies popUpTo(graph.findStartDestination().id) { saveState = true }; launchSingleTop = true; restoreState = true unit ./gradlew :composeApp:commonTest --tests "*NavigationTest*" W0 pending
V-02 TBD 1 UI-04 GlassSurface selects Liquid backend on iOS source set at compile time unit ./gradlew :composeApp:commonTest --tests "*GlassBackend*" W0 pending
V-03 TBD 1 UI-04 GlassSurface debug-toggle flow honors multiplatform-settings value via MapSettings test impl unit ./gradlew :composeApp:commonTest --tests "*GlassBackendOverride*" W0 pending
V-04 TBD 1 UI-09 App.kt AuthState.Authenticated + currentUser != null resolves to AppShell, not PostLoginPlaceholderScreen unit ./gradlew :composeApp:commonTest --tests "*AppShellGateTest*" W0 pending
V-05 TBD 1 UI-10 RecipesSearchViewModel: open() → onQueryChange("foo") → close() clears query and resets isOpen unit ./gradlew :composeApp:commonTest --tests "*RecipesSearchViewModelTest*" W0 pending
V-06 TBD 1 UI-10 RecipesSearchViewModel: clear() resets only query, keeps isOpen=true unit (same target) W0 pending
V-07 TBD 1 UI-10 PantrySearchViewModel: parity with recipes (open/close/clear semantics) unit ./gradlew :composeApp:commonTest --tests "*PantrySearchViewModelTest*" W0 pending
V-08 TBD 1 UI-09 / UI-03 Each tab renders its own empty state on first launch without flash manual smoke (iOS) n/a manual pending
V-09 TBD 1 UI-03 Bottom-tab reselect preserves nested back stack manual smoke (iOS) n/a manual pending
V-10 TBD 1 UI-10 Search affordance visible on Recipes + Pantry tabs only (D-06) manual smoke + screenshot per tab n/a manual pending
V-11 TBD 1 UI-04 Liquid dock/menu chrome animates on iOS device path; flat fallback path activates when override is set manual smoke (iOS) n/a manual pending

Status: pending · green · red · ⚠️ flaky


Wave 0 Requirements

  • composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/navigation/NavigationTest.kt — stubs for V-01 (UI-03 navigateToTab semantics; uses TestNavHostController if available, else asserts on the option-builder lambda)
  • composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/components/glass/GlassBackendTest.kt — stubs for V-02 (UI-04 compile-time backend selection)
  • composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/components/glass/GlassBackendOverrideTest.kt — stubs for V-03 (UI-04 settings-driven debug override)
  • composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/screens/shell/AppShellGateTest.kt — stubs for V-04 (UI-09 App.kt routing)
  • composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesSearchViewModelTest.kt — stubs for V-05/V-06 (UI-10)
  • composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/screens/pantry/PantrySearchViewModelTest.kt — stubs for V-07 (UI-10 mirror)
  • iOS-simulator smoke runbook (see § Manual-Only Verifications) committed alongside the phase artifacts so V-08…V-11 have a repeatable check
  • No new framework install — kotlin.test is already wired through recipe.kotlin.multiplatform convention plugin
  • Wave 0 dependency-resolution checks for the three load-bearing assumptions A1/A2/A3 (Liquid iOS klib resolves, Material Icons Outlined available without material-icons-extended, nav-compose 2.9.2 K/N back-stack save/restore on iOS)

Manual-Only Verifications

Behavior Requirement Why Manual Test Instructions
Each tab's empty state renders without flash on first launch UI-09 Compose Multiplatform on iOS lacks mature snapshot/UI testing for chrome-level visual verification iOS sim cold launch → land on Planer (default tab) → confirm intentional empty illustration + copy → no spinner/flash
Tab back-stack preserved across reselection UI-03 Real navigation behavior across nested NavHosts is best validated visibly on the simulator Navigate Przepisy → tap any future stub detail nav → switch to Spiżarnia → switch back to Przepisy → expect previous state restored, not start dest
Search affordance is functional and scoped UI-10 UX of opening/closing/clearing must be felt, not just unit-asserted On Recipes tab: tap search icon → surface opens → type "abc" → confirm query state → tap clear → query empty, surface still open → tap close → surface dismissed. Repeat on Pantry. Confirm Planer/Zakupy do NOT show search affordance.
Liquid dock/menu chrome on iOS device path UI-04 Glass aesthetic and performance can only be judged by eye iOS sim run with default config → confirm Liquid menu/dock renders with the expected glass treatment → toggle debug override via multiplatform-settings storage → confirm flat fallback activates
Dock collapse animation on tab change UI-04 / UI-09 Animation feel Tab between all four destinations → confirm dock animation runs smoothly, no jank

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 (test file stubs above)
  • No watch-mode flags
  • Feedback latency < 90s for commonTest
  • nyquist_compliant: true set in frontmatter

Approval: pending