Files

9.7 KiB
Raw Permalink Blame History

phase, verified, status, verdict, score, plans_complete
phase verified status verdict score plans_complete
02.1-app-shell-navigation-search-foundation 2026-05-08T00:00:00Z passed PASS 5/5 success criteria verified 8/8

Phase 2.1 Verification Report — App Shell, Navigation & Search Foundation

Phase Goal: Build the app shell, navigation, and search foundation — type-safe nav graphs, glass design tokens, glass surface primitive, dock + search chrome, per-tab search VMs, empty-state tab screens, and final Koin/integration wiring.

Verdict: PASS

All 5 ROADMAP success criteria verified, all 7 V-anchor automated tests present without @Ignore, iOS compile + linkDebugFrameworkIosSimulatorArm64 green, and 8/8 plans executed with summaries.


ROADMAP Success Criteria

# Criterion (paraphrased) Status Evidence
1 Authenticated user lands in shell, can switch between 4 tabs without signing out PASS App.kt:66-69 routes RootRoute.Shell -> AppShell(); AppShell.kt hosts RootNavHost with 4 nested graphs; DockBar calls navigateToTab(dest.graphRoute)
2 Each tab has its own back-stack boundary; intentional empty states PASS RootNavHost.kt uses 4 navigation<*Graph>(startDestination = *Home) blocks; NavExtensions.navigateToTab applies popUpTo(...){saveState=true}; launchSingleTop=true; restoreState=true (V-01); Tab*Screen composables render EmptyState with anticipatory Polish copy
3 Compose Unstyled / renderless primitives, Material 3 only legacy PASS New shell composables use BasicText/BasicTextField from compose-foundation; zero androidx.compose.material3 imports in shell/dock/search/glass/empty packages (per executor reports); MaterialTheme retained only in RecipeTheme.kt for legacy auth screens
4 Liquid library used for chrome with fallback path PASS GlassSurface.kt dispatches via LocalGlassBackend to LiquidGlassSurface / HazeGlassSurface / FlatGlassSurface; GlassBackend.kt has resolveGlassBackend(settings, isDebugBuild, default) with debug override (V-02, V-03); registered as single<GlassBackend> in ShellModule.kt defaulting to Liquid
5 Search button functional: open/close/clear/query echo, intentional empty body PASS RecipesSearchViewModel / PantrySearchViewModel expose open/close/onQueryChange/clear with locked semantics (close clears query, clear preserves isOpen) — covered by V-05/V-06/V-07 tests; SearchPill.kt is a 44dp inline pill with BasicTextField + clear/close icons; FloatingSearchButton gated to Recipes/Pantry only

Validation Anchors V-01..V-07

Anchor Test File Status
V-01 commonTest/.../navigation/NavigationTest.kt Real assertions (no @Ignore) — 3 cases passing
V-02 commonTest/.../ui/components/glass/GlassBackendTest.kt Real assertions — backend default + parsing
V-03 commonTest/.../ui/components/glass/GlassBackendOverrideTest.kt Real assertions — debug override + production short-circuit
V-04 commonTest/.../ui/screens/shell/AppShellGateTest.kt Real assertions — 5 AuthState×hasUser cases via pure resolveRootRoute
V-05 commonTest/.../ui/screens/recipes/RecipesSearchViewModelTest.kt Real assertions — 5 cases (open/query/close clears, etc.)
V-06 (same file as V-05) Real assertions — clear() resets only query, isOpen=true
V-07 commonTest/.../ui/screens/pantry/PantrySearchViewModelTest.kt Real assertions — 3 cases parity with Recipes

grep -r '@Ignore' composeApp/src/commonTest/0 results (all Wave-0 stubs replaced with real assertions).


Build & Test Verification (this verification run)

  • ./gradlew :composeApp:iosSimulatorArm64Test --tests "dev.ulfrx.recipe.navigation.*" --tests "dev.ulfrx.recipe.ui.components.glass.*" --tests "dev.ulfrx.recipe.ui.screens.shell.*" --tests "dev.ulfrx.recipe.ui.screens.recipes.RecipesSearchViewModelTest" --tests "dev.ulfrx.recipe.ui.screens.pantry.PantrySearchViewModelTest"BUILD SUCCESSFUL
  • ./gradlew :composeApp:linkDebugFrameworkIosSimulatorArm64BUILD SUCCESSFUL (iOS sim framework link green)

Required Artifacts (existence + substantive)

All files referenced in the 8 plan SUMMARYs exist on disk.

Theme tokens (Plan 02.1-02)

  • ui/theme/RecipeColors.kt — semantic light/dark palette
  • ui/theme/RecipeTypography.kt — display/title/body/label scale
  • ui/theme/RecipeSpacing.kt — xs/sm/lg/xl/xxl/xxxl
  • ui/theme/RecipeShapes.kt — pill/circle radii
  • ui/theme/RecipeGlass.kt — border/shadow/blur defaults
  • ui/theme/RecipeTheme.kt — providers + MaterialTheme wrapper + LocalGlassBackend wiring

Glass primitive (Plan 02.1-03)

  • ui/components/glass/GlassBackend.kt — enum, CompositionLocal, resolver
  • ui/components/glass/GlassSurface.kt — public dispatcher
  • LiquidGlassSurface.kt, HazeGlassSurface.kt, FlatGlassSurface.kt — three backends
  • GlassBackdrop.kt — shared sampling source
  • IsDebugBuild.kt (common) + .ios.kt + .android.kt (actuals)

Navigation (Plan 02.1-04)

  • navigation/Routes.kt — 8 @Serializable data object (4 graph + 4 home)
  • navigation/BottomBarDestination.kt — enum in D-03 order, hasSearch flag
  • navigation/RootNavHost.kt — single root with 4 nested navigation<*Graph> blocks
  • navigation/NavExtensions.ktnavigateToTab four-flag contract

Shell composables (Plan 02.1-05)

  • ui/screens/shell/ShellViewModel.kt — (activeTab, searchOpen) StateFlow
  • ui/screens/shell/AppShell.kt — authenticated root, GlassBackdropSource + bottom chrome column
  • ui/components/dock/DockBar.kt — collapsible 4-tab dock with animateContentSize + AnimatedContent
  • ui/components/dock/FloatingSearchButton.kt — 44dp glass button

Search (Plan 02.1-06)

  • ui/screens/recipes/RecipesSearchViewModel.kt — open/close/onQueryChange/clear + nullable SearchSource hook
  • ui/screens/pantry/PantrySearchViewModel.kt — parity
  • ui/components/search/SearchPill.kt — 44dp inline GlassSurface pill with BasicTextField

Empty state + tab screens (Plan 02.1-07)

  • ui/components/empty/EmptyState.kt — reusable composable with mergeDescendants a11y
  • ui/screens/{planner,recipes,pantry,shopping}/{*Screen,*ViewModel}.kt — 8 files

Final integration (Plan 02.1-08)

  • di/ShellModule.kt — Koin: GlassBackend single + ShellViewModel + 4 tab VMs + 2 search VMs
  • di/AppModule.kt modified: includes(authModule, userModule, shellModule)
  • App.kt modified: RootRoute enum + resolveRootRoute() + Authenticated → AppShell()
  • RootNavHost.kt modified: TabHomePlaceholder calls replaced with real Tab*Screen via koinViewModel(viewModelStoreOwner = parent)

Resource Strings (i18n hygiene)

composeResources/values/strings.xml carries 24 keys total: 7 auth (pre-existing) + 4 shell tabs + 2 search placeholders + 3 search a11y + 8 empty-state. Zero hardcoded Polish literals in new .kt files (all flow through stringResource(Res.string.*)) — satisfies UI-01 and convention #9.


Key Wiring Verification

Link Status Evidence
App.ktAppShell (auth gate) WIRED App.kt:13 import + App.kt:69 RootRoute.Shell -> AppShell()
AppModule.ktshellModule WIRED AppModule.kt:11 includes(authModule, userModule, shellModule)
RootNavHost → 4 Tab Screens WIRED koinViewModel<*ViewModel>(viewModelStoreOwner = parent) per tab; no TabHomePlaceholder left
RecipeThemeLocalGlassBackend WIRED RecipeTheme.kt includes LocalGlassBackend provides koinInject<GlassBackend>()
DockBar tab cell → navigateToTab WIRED AppShell dispatches navigateToTab(dest.graphRoute) on tab change
AppShellSearchPill + per-tab Search VM WIRED When-branches for both Recipes and Pantry; gated by activeTab.hasSearch

Anti-Patterns Scan

  • Zero androidx.compose.material3 imports in new shell/dock/search/glass/empty packages (executor reports + spot-checks).
  • Zero direct liquid / haze imports outside the dedicated backend files.
  • No safeContentPadding() in AppShell (Pitfall F honored).
  • No hardcoded Polish literals in commonMain .kt files.
  • No TODO(02.1-08) markers remain after Plan 08.

Out-of-Scope / Acknowledged Items

  1. Pre-existing Spotless violations in 38 unrelated files (LokksmithOidcSupport, OidcClient, AuthSession, etc.) — confirmed by Plan 08 executor via git stash + spotlessCheck to predate this phase. OUT OF SCOPE for Phase 2.1; flagged for a future cleanup pass. Does not affect Phase 2.1 verdict.
  2. Manual iOS-simulator smoke tests V-08..V-11 (visual chrome, animation feel, search affordance UX, Liquid look-and-feel) — deferred to user smoke-test pass per VALIDATION.md (no simulator in autonomous run). Static checks confirm code paths are wired correctly; visual confirmation belongs to the user's manual runbook execution.
  3. ./gradlew :composeApp:check is RED only because of the 38-file pre-existing Spotless debt. The Phase 2.1 owned files all pass Spotless (Plan 08 commit a6f0d46).

Regression Check

  • Phase 2 auth flow preserved: LoginScreen / SplashScreen / MaterialTheme wrapper untouched in core paths.
  • PostLoginPlaceholderScreen.kt and PostLoginViewModel.kt source files preserved on disk per CONTEXT line 101 (logout-bridge possibility), only their imports/call site removed from App.kt.
  • No deletions to auth/ or user/ packages; the brief Plan 01 unrelated-staged-file accident was repaired in commit 1066e9b before any other work.

Gaps

None. All 5 ROADMAP success criteria, all V-01..V-07 anchors, and all 8 plans are complete and substantive. Phase 2.1 is ready to mark done.


Verified: 2026-05-08 by gsd-verifier (Claude) Phase: 02.1-app-shell-navigation-search-foundation