10 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, decisions, metrics, requirements
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | decisions | metrics | requirements | |||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02.1 | 08 | app-shell-final-integration |
|
|
|
|
|
|
|
|
|
Phase 02.1 Plan 08: Final Integration Summary
Wire the seven shell ViewModels and the GlassBackend resolver into a Koin shellModule, extend appModule.includes, provide LocalGlassBackend through RecipeTheme, replace the four TabHomePlaceholder stubs in RootNavHost with the real Tab*Screen composables, and swap App.kt's Authenticated branch from PostLoginPlaceholderScreen to AppShell — closing UI-09.
What landed
Task 1 — ShellModule + AppModule + RecipeTheme glass provider — 9714765
- New
di/ShellModule.ktregisters:single<GlassBackend> { resolveGlassBackend(get<Settings>(), isDebugBuild, default = GlassBackend.Liquid) }viewModel<ShellViewModel>()- 4 tab VMs (
Planner/Recipes/Pantry/Shopping) - 2 search VMs (
RecipesSearchViewModel/PantrySearchViewModel)
di/AppModule.ktextended:includes(authModule, userModule, shellModule)ui/theme/RecipeTheme.ktadds one newprovidesentry —LocalGlassBackend provides koinInject<GlassBackend>()— at the same level as the other Recipe locals. TheMaterialTheme(...)wrapper is preserved unchanged so legacy auth screens (Login / PostLoginPlaceholder / Splash) keep resolvingMaterialTheme.colorScheme.*(Open Question Q3 RESOLVED).- Settings binding: registered in
auth/IosAuthModule.ktandauth/AndroidAuthModule.kt(Phase 2 wiring for SecureAuthStateStore) — reused by shellModule, no commonMain Settings binding was needed.
Task 2 — RootNavHost wires real tab screens — 20e840e
- All four
TabHomePlaceholder(...)calls replaced withkoinViewModel<*ViewModel>(viewModelStoreOwner = parent)lookups followed by the real*Screen(viewModel = vm)calls. private fun TabHomePlaceholder(...)deleted; placeholder imports (BasicText,Box) removed.- All four
TODO(02.1-08)markers cleared. - Each tab's ViewModelStoreOwner remains the parent graph's
NavBackStackEntry, so tab VMs survive across home-detail navigations within the graph (RESEARCH § Pattern 2).
Task 3 — App.kt routes Authenticated to AppShell — 2639244
- New top-level
enum class RootRoute { Splash, Login, Shell }andinternal fun resolveRootRoute(authState, hasCurrentUser): RootRoute. App()body now switches onresolveRootRoute(authState, currentUser != null)with three branches: Splash / Login / Shell. Authenticated + user goes toAppShell(); Authenticated + null user still holds onSplashScreen().LaunchedEffect(authSession) { initialize() }and theRecipeTheme { ... }wrapper preserved verbatim.PostLoginPlaceholderScreen.ktandPostLoginViewModel.ktsource files remain on disk (CONTEXT line 101 / Open Question Q3 RESOLVED — logout-bridge possibility). Only their imports and the call site in App.kt are removed.
Task 4 — AppShellGateTest backed by real assertions (V-04) — 26392df
@Ignoreremoved; five assertions cover allAuthState × hasCurrentUsercombinations:Authenticated + user → Shell(V-04 anchor)Authenticated + null user → Splash(two-layer gate)Unauthenticated → LoginLoading → SplashLoading + stale user → Splash(defensive)
- Tests run through the pure
resolveRootRoutehelper, sidestepping the immature CMP iOS Compose UI testing surface (VALIDATION.md line 27). - All Wave-0
@Ignorestubs across the phase are now backed by real assertions:grep -r '@Ignore' composeApp/src/commonTest/returns 0.
Task 5 — spotless formatting — a6f0d46
- Spotless reformatted plan-08 files (App.kt, RootNavHost.kt, RecipeTheme.kt) —
multi-line function signature for
resolveRootRoute, multi-linerememberblocks. Only changes to plan-08 files committed; pre-existing spotless violations in unrelated files (LokksmithOidcSupport, OidcClient, AuthSession, etc.) left out of scope per Rule SCOPE BOUNDARY — those failures predate this plan and require their own cleanup pass.
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 — Blocking import] Initial ShellModule.kt used
org.koin.core.module.dsl.viewModel which expects a definition lambda; the
no-arg viewModel<T>() form lives in org.koin.plugin.module.dsl.viewModel
(matching auth/AuthModule.kt).
- Files modified:
di/ShellModule.kt - Resolved before any commit; rolled into Task 1.
2. [Rule 3 — Blocking lint] Spotless reformatted plan-08 files (multi-line
function param lists, multi-line remember blocks). The wider repo has 38
pre-existing spotless violations in unrelated files; per scope boundary, only
the in-scope formatting was committed (a6f0d46). The pre-existing violations
were confirmed to predate this plan via git stash + spotlessCheck before
the plan's edits.
RecipeTheme.kt edit (final form)
The single in-scope change was adding the LocalGlassBackend provides glassBackend
entry alongside the existing four LocalRecipe* entries:
@Composable
public fun RecipeTheme(content: @Composable () -> Unit) {
val dark = isSystemInDarkTheme()
val recipeColors = if (dark) DarkRecipeColors else LightRecipeColors
val materialColors = if (dark) LegacyMaterialDarkColors else LegacyMaterialLightColors
val glassBackend = koinInject<GlassBackend>()
MaterialTheme(colorScheme = materialColors) {
androidx.compose.runtime.CompositionLocalProvider(
LocalRecipeColors provides recipeColors,
LocalRecipeTypography provides DefaultRecipeTypography,
LocalRecipeSpacing provides DefaultRecipeSpacing,
LocalRecipeShapes provides DefaultRecipeShapes,
LocalRecipeGlass provides DefaultRecipeGlass,
LocalGlassBackend provides glassBackend,
content = content,
)
}
}
The MaterialTheme(colorScheme = materialColors) wrapper is unchanged (Open
Question Q3 RESOLVED — legacy auth screens still depend on it).
Settings registration check
com.russhwolf.settings.Settings is bound as a single<Settings> in:
composeApp/src/iosMain/kotlin/dev/ulfrx/recipe/auth/IosAuthModule.kt:25composeApp/src/androidMain/kotlin/dev/ulfrx/recipe/auth/AndroidAuthModule.kt:19
Phase 2 introduced this for SecureAuthStateStore. shellModule reuses the same
binding — no commonMain single<Settings> was required.
PostLoginPlaceholderScreen / PostLoginViewModel preservation
Both source files remain on disk:
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/auth/PostLoginPlaceholderScreen.kt
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/auth/PostLoginViewModel.kt
They are no longer reachable from App.kt — kept as a logout-bridge possibility
per CONTEXT line 101 / Open Question Q3 (RESOLVED). A future phase may revive
or repurpose them.
Manual smoke (V-08 / V-09 / V-10 / V-11)
Manual iOS-simulator smoke deferred — no simulator in this autonomous run. Static checks performed:
./gradlew :composeApp:compileKotlinIosSimulatorArm64 -q→ exits 0./gradlew :composeApp:compileDebugKotlinAndroid -q→ exits 0./gradlew :composeApp:iosSimulatorArm64Test --tests "...AppShellGateTest"→ tests passgrep -r '@Ignore' composeApp/src/commonTest/→ 0 results
./gradlew :composeApp:check is RED only because of pre-existing spotless
violations in 38 unrelated files (predates this plan; confirmed via stash).
Self-Check: PASSED
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/di/ShellModule.kt — FOUND
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/di/AppModule.kt — FOUND (modified)
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/App.kt — FOUND (modified)
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/navigation/RootNavHost.kt — FOUND (modified)
- composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/theme/RecipeTheme.kt — FOUND (modified)
- composeApp/src/commonTest/kotlin/dev/ulfrx/recipe/ui/screens/shell/AppShellGateTest.kt — FOUND (un-ignored)
- Commit 9714765 — FOUND (Task 1)
- Commit 20e840e — FOUND (Task 2)
- Commit 2639244 — FOUND (Task 3)
- Commit 26392df — FOUND (Task 4)
- Commit a6f0d46 — FOUND (style)