Skip to Content
Design systemPrototype implementation status

Prototype Implementation Status

Maps every screen and sub-view in the ~/Desktop/Oter design prototypes to its implementation in the KMP app, per platform. Companion to Tokens and design-system-conventions.md. Follow-ups: tech-debt/design-system.md, tech-debt/migrations.md.

Two UI stacks (read this first)

StackPlatformsEntry / navhostNotes
SharedAndroid · iOS · Web (WASM)SharedAppNavHost (shared/.../navigation/AppNavHost.kt); hosts androidApp/.../MainActivity.kt, composeApp/iosMain/.../IOSApp.kt, composeApp/wasmJsMain/.../App.ktOne commonMain code path → Android, iOS and Web are identical. The prototype rebuilds live here.
DesktopDesktop (JVM)composeApp/desktopMain/.../ui/navigation/AppNavHost.kt + per-feature *DestinationsReuses the shared Dashboard, Notifications, Work-Jobs-Hub, Statistics and Nutrition prototype rebuilds (the desktop-only older StatisticsScreen/RecipesScreen + destinations were removed). Command / Search / Focus / Onboarding routes are now registered, with first-launch onboarding gating wired into the desktop navhost (reads OnboardingPreferences, routes un-onboarded users into the wizard). The topbar bell now opens a dropdown-panel variant.

Because the shared stack is one code path, the per-view tables below use a single Shared (A·iOS·Web) column plus a Desktop column. “Shared ✅” therefore means done on Android and iOS and Web.

Status legend: ✅ done (prototype-faithful, wired) · 🟡 partial · ◐ present but a different/older screen, or limited wiring · ⬜ not present.

Top-level matrix (all four platforms explicit)

Prototype featureAndroidiOSWebDesktop
Login / Auth✅✅✅✅
Dashboard✅✅✅✅
Tasks✅✅✅✅
Habits✅✅✅✅
Journal✅✅✅✅
Books✅✅✅✅
Calendar✅✅✅✅
Finance home✅✅✅✅
Finance sub-screens (stats/category-mapper/product-prices)✅✅✅✅
Finance sub-screens (receipt/reconcile/importer/prediction)✅✅🟡✅
Workout (+ history)✅✅✅✅
Study✅✅✅✅
Settings✅✅✅✅
Projects✅✅✅✅
Tags · Timers · Pomodoros · Chat✅✅✅✅
Statistics✅✅✅✅
Notifications✅✅✅✅
Onboarding✅✅✅✅
Command palette✅✅✅✅
Search🟡🟡🟡🟡
Focus✅✅✅✅
Work🟡🟡🟡✅
Nutrition✅✅✅✅

Per-platform tally — Android = iOS = Web: 19 ✅ · 2 🟡 (Work, Search) + the transaction-backed finance sub-screens still stubbed (ProductPrices/ReceiptScan/Reconcile/Importer/BalancePrediction). Desktop now wires Command / Search / Focus / Onboarding / WorkTimeLog / WorkStats + the Work role typed-routes (Jobs Hub stays panel-based, which is richer than the mobile drill-downs). Statistics & Nutrition render the shared prototype rebuilds on desktop; FinanceStatistics + CategoryKeywordMapper are now real on the shared stack (were “coming soon” stubs).

Update: TransactionViewModel was migrated to commonMain (shared/.../ui/viewmodels/TransactionViewModel.kt, converted from androidx ViewModel to the plain-class scope pattern, registered single{} in all four platform DI modules; desktop call sites now use koinInject). ProductPrices is now the real screen on the shared stack. ReceiptScan and Reconcile are now functional on iOS — the iOS picker actuals are implemented (UIImagePickerController for receipts, UIDocumentPickerViewController for bank statements; ReceiptImageBitmap.ios.kt now decodes via skiko). Android pickers were already real. Bank-statement picker now real on all four platforms — the desktop/JVM BankStatementFilePicker.jvm.kt actual (which previously returned emptyList(), breaking the Finance Importer on desktop) now uses a multi-select JFileChooser (csv/txt/tsv, read as text to match the Android actual). Android, iOS and Web (wasmJs) pickers were already implemented. FinanceImporter + BalancePrediction are now also wired on the shared navhost (Screen.FinanceImporter / Screen.FinanceBalancePrediction).


Per-view breakdown (done / not done, by platform)

Work — WorkScreen.kt + role screens + services/work/*

Desktop registers only Screen.Work (→ shared Jobs Hub); none of the role routes are in the desktop navhost.

View / sub-screenShared (A·iOS·Web)Desktop
Jobs Hub (cards by type)✅✅
New-job dialog✅✅
Time Log (day-grouped, filter, billable summary)✅⬜
Log-session dialog (createSession)✅⬜
Time Stats (KPIs, billable split, by-activity)✅⬜
SE — Tickets kanban (6 columns)✅⬜
SE — Ticket detail + interactive checklist✅⬜
SE — Overview tab⬜⬜
SE — Team tab⬜⬜
CS — Clients & Projects✅⬜
CS — Project detail (milestones + billing breakdown)⬜⬜
Store — Overview (launch countdown · P&L summary · profile · areas)✅⬜
Store — Tasks tab✅⬜
Store — Inventory tab (on-hand/reorder + low-stock + KPIs)✅⬜
Store — Suppliers tab✅⬜
Store — Shifts tab✅⬜
Store — P&L line-item detail⬜⬜
Inventory stock-events sparkline⬜⬜
Dialogs: new ticket / client / project / milestone / supplier / product / stock-event / shift⬜⬜
Live running-timer (start/stop)⬜⬜

Nutrition — NutritionScreen.kt + services/meals/NutritionViewModel.kt

Desktop now renders the shared NutritionScreen (the desktop navhost calls it directly under Screen.Meals.route), so all Nutrition views are at parity across all four platforms.

View / sub-screenShared (A·iOS·Web)Desktop
Today dashboard (calorie ring + protein/carbs/fat bars)✅✅
Meals grouped by tag + consume/undo✅✅
Library tab (getAllRecipes + meal-tag filters + day badges)✅✅
Week tab (7-day plan, today highlighted)✅✅
History tab (getRecipeTracksByDateRange 7/14/30d + vs-goal)✅✅
Add-food / edit / delete recipe + skip-with-alternative + barcode-scan dialogs (AddEditRecipeDialog / SkipMealDialog / BarcodeLookupDialog; camera capture still manual-entry)✅✅
Per-user macro goals (vs prototype defaults)⬜⬜

Statistics — StatisticsScreen.kt

Desktop now renders the shared StatisticsScreen (the desktop navhost calls it directly under Screen.Statistics.route with the shared StatisticsViewModel), at parity across all four platforms.

View / sub-screenShared (A·iOS·Web)Desktop
KPI strip (+ week-over-week deltas: Tasks/Habit-rate/Workouts)✅✅
Weekly-activity stacked bars✅✅
Activity-by-area donut✅✅
Module completion bars · Top streaks✅✅
Range switcher (Day / Week / Month / Year — StatRange + OterSegmentedControl → setRange, /statistics?range=)✅✅
Real focus / work time aggregation (Time-spent bars over StudySessions + WorkSessions)✅✅

Notifications — NotificationsScreen.kt + services/notifications/*

View / sub-screenShared (A·iOS·Web)Desktop
Inbox page (filter chips, day groups, typed rows)✅✅
Mark-read / mark-all-read (POST /notifications/read-all)✅✅
Topbar bell + unread badge➖ (mobile nav)✅
Topbar dropdown-panel variant (prototype)➖ (mobile nav)✅

Onboarding — OnboardingScreen.kt + preferences/OnboardingPreferences

View / sub-screenShared (A·iOS·Web)Desktop
5-step wizard (Welcome→Areas→Goals→Connect→Done)✅✅
First-launch gating (start destination)✅✅
Persistence (DataStore / localStorage)✅✅
Per-day prompt source⬜⬜

Command / Search / Focus

ScreenShared (A·iOS·Web)Desktop
Command palette (CommandScreen.kt)✅✅
Search shell (SearchScreen.kt, quick-access + live /search; results deep-link to task/habit/book/recipe/study/journal detail screens; multi-term AND matching + relevance ranking server-side; a true tsvector/GIN index is still a future optimization)✅✅
Focus timer (FocusScreen.kt, ring + presets)✅✅

Dashboard — DashboardScreen.kt (wired on all four platforms)

Card / elementShared (A·iOS·Web)Desktop
Greeting + responsive Up-Next + KPI strips✅✅
Tasks · Habits · Books cards✅✅
Nutrition card (calorie ring + carbs/fat macro bars)✅✅
Workout card (+ next-session highlight)✅✅
Finance card (+ month-net delta line)✅✅
Journal card (+ per-entry mood dots)✅✅

Tasks (mobile) — TasksScreen.kt + NewEditTaskMobileSheet.kt + TaskDetailScreen.kt

The shared (mobile) Tasks editor was name+priority only; it’s now at field parity with the desktop dialog. The desktop reuses its own richer NewEditTaskDialog (slot-injected) and is unchanged.

View / capabilityShared (A·iOS·Web)Desktop
Create / edit: name · priority✅✅
Note✅✅
Due & Scheduled date+time (real CustomDatePicker/CustomTimePicker)✅✅
Subtasks (add / toggle / remove)✅✅
Reminders (offset presets)✅✅
Tags (multi-select from TagsViewModel)✅✅
Project (picker from ProjectsViewModel)✅✅
Detail screen (status · priority · dates · note · tags · subtasks toggle · reminders + complete/edit/delete)✅ (TaskDetailRoute)✅ (TaskDetailScreen)
AI import · AI tag organize · view modes (incl. Eisenhower matrix) · showInListFrom✅✅

Create path carries reminders + tags via the extended TaskRepository/TaskService.addTask (+ TasksViewModel.createTask). ProjectApiService is now registered in the Android/iOS DI modules (was desktop/web-only), which the project picker requires.

Habits (mobile) — HabitsScreen.kt (DefaultAddEditHabitDialog) + HabitDetailScreen.kt

The shared habit dialog was name+frequency only; now full. Desktop reuses its own NewEditHabitDialog (already had reminders + subtasks) and is unchanged — a habit tap still opens edit there (the new onOpenDetail is mobile-only).

View / capabilityShared (A·iOS·Web)Desktop
Create / edit: name · frequency✅✅
Note✅✅
Time (real CustomTimePicker)✅✅
Reminders (offset presets)✅✅
Subtasks (add / toggle / remove)✅✅
Detail screen (frequency · streak · time · note · subtasks toggle · reminders + check-in/edit/delete)✅ (HabitDetailRoute)✅ (HabitDetailRoute → shared HabitDetailScreen via HabitDetailDestination)
Per-day check-in history (30-day heatmap, GET /habits/{id}/history)✅✅
Background reminder service⬜◐ (desktop reminder manager)

Pre-existing features (reused, wired on both stacks)

These were migrated before this program; both stacks register their routes. Sub-views (detail screens, dialogs) are pre-existing and not re-audited here — except Tasks and Habits, whose mobile editors + detail screens were brought to desktop parity (see the two sections above).

FeatureShared routesDesktop routes
Authauthauth (own Login/SignUp)
Tasks (+ detail)tasks + TaskDetailRoute (now on the shared stack too)tasks + TaskDetailRoute
Habits (+ detail)habits + HabitDetailRoute (new on shared)habits
Journaljournaljournal
Booksbooksbooks
Calendarcalendarcalendar
Financefinance + importer/receipt/prices/reconcile/statistics/predictionsame + transaction/budget/scheduled detail routes
Workoutworkout, workout_historysame
Studystudystudy
Settingssettingssettings (own)
Projectsprojectsprojects
Tags · Timers · Pomodoros · Chat · CategoryKeywordMapperall wiredall wired (+ TimerListDetailRoute)

Remaining work (tracked follow-ups)

2026-06-21 — mobile (Android) parity hardening: sweep across foundations + Tasks/Habits/Finance Importer + Timers + Pomodoros + Books to close functional gaps. All four targets (:composeApp:compileAndroidMain, :composeApp:compileKotlinDesktop, :composeApp:compileKotlinWasmJs, :shared:compileKotlinIosArm64) green; :androidApp:assembleDebug produces a working APK.

  • Foundations on Android — feature-gate composables (FeatureGuard.kt, AppUnavailableScreen.kt, ConfigLoadingShimmer.kt) were desktop-only; moved to shared/src/commonMain/.../ui/components/ so the shared navhost can guard routes the same way desktop does. SharedAppNavHost now wraps every gated route in a gated("KEY") helper that calls RequireFeatureScreen with the same feature-key map desktop uses. androidApp/.../activities/MainActivity.kt now collects ThemeViewModel.isDarkTheme + AppLocaleViewModel.languageTag and wraps the content in key(languageTag) { OterTheme(darkTheme = …) } — Android can now honor dark/light + locale changes. AppViewModel + AppIntent + GuidanceViewModel + BalancePredictionViewModel were desktop-only; moved to shared/commonMain (BalancePrediction swapped JVM-only System.currentTimeMillis() / java.util.UUID for kotlin.time.Clock + kotlin.uuid.Uuid under @file:OptIn) and registered in AndroidAppModule.kt.
  • Finance Importer is no longer a stub — FinanceImporterScreen in shared/.../ui/screens/SubScreens.kt was a 5-line “coming soon” placeholder. TransactionImportScreen (the real CSV/Lines parse + account picker + preview + type overrides + skip-duplicates flow) moved from composeApp/.../desktopMain/.../ui/screens/ to shared/src/commonMain/.../ui/screens/ (same FQN). Switched the AccountViewModel import inside the screen from the desktop ui.viewmodels package to the shared com.esteban.ruano.oter.viewmodel package; registered the shared AccountViewModel on desktop too so the dual-namespace coexists. The stub SubScreens.kt was deleted.
  • Timer notifications on mobile — TimerListDetailMobileDestination hardcoded listNotifications = emptyList() and onGetTimerNotifications = { emptyList() }. The mobile TimersViewModel now owns notifications (sourced from webSocketClient.timerNotifications, mirroring desktop), listNotifications (filtered by the open list id), updateListNotifications(listId), and getTimerNotifications(timerId). The destination collects the flow + invokes the loader in LaunchedEffect(timerListId).
  • Pomodoros VM-backed — TimersViewModel constructor gained pomodoroService: PomodoroService (Android, iOS, wasmJs DI all updated from TimersViewModel(get(), get()) → TimersViewModel(get(), get(), get())) plus pomodoros / pomodorosLoading / pomodorosError flows + loadPomodoros() / removePomodoro(id) / resetPomodorosError(). The Pomodoros route in SharedAppNavHost dropped its inline LaunchedEffect { pomodoroService.getPomodoros() } and uses VM state instead.
  • Tasks + Habits behavior parity on mobile — TaskDetailScreen now surfaces a time-tracking history section (uses the shared TimeTrackingHistorySection + TimeTrackingService.getSessionsForTask, refreshes on DomainEvent.TimerSessionStopped) and a three-color due-date indicator (danger / warning / primary by days-until). The mobile TasksScreen overload now wires customRangeStartDate / customRangeEndDate / onCustomRangeChange + a customRangePicker slot (using the shared CustomDatePicker); onReschedule opens the same date picker and preserves the existing HH:mm suffix on dueDateTime; onToggleTaskSubtask flips subtasks inline via tasksViewModel.updateTask. The mobile HabitsScreen overload passes virtualHabitRows after switching HabitsViewModel.getHabits() to getByDateRangeWithMeta (the metadata-bearing endpoint desktop already used), wires onToggleHabitSubtask inline, and accepts an onOpenVirtualFeature map that routes VirtualFeatureKind.WORKOUT/BOOK/STUDY/JOURNAL to the matching screens.
  • BooksService on Android — was missing from AndroidAppModule.kt; BookDetailScreen (reached from Books, Search, or the typed BookDetailRoute) called koinInject<BooksService>() and would have crashed with NoBeanDefFoundException. Now registered next to RecipesService / StudyService / WorkoutService with the same single { services.books.BooksService(get(), PlatformConfig.BASE_URL) } shape desktop uses.

Verdict: the shared (Android/iOS/Web) stack is now at functional parity with desktop for every route both platforms target. Deliberately desktop-only items — the 6 typed Finance CRUD routes (FinanceNewTransactionRoute, etc.) and the Work role-specific routes already on the matrix — remain by design; mobile users reach the same operations via FinanceScreen.kt’s inline showTransactionForm / showScheduledTransactionForm / showSavingsGoalForm / showAccountForm / showDebtForm dialogs and the Work feature drill-downs.

2026-06-11 — parity-closure round (real asymmetries found in a fresh code audit):

  • Principles on Web (fixed) — DashboardPrinciplesSection is in commonMain (renders on every platform) but PrincipleService/PrinciplesViewModel were only registered in the Android/iOS/Desktop DI modules; they’re now registered in composeApp/src/wasmJsMain/.../di/WebModules.kt too (was a wasmJs DI crash).
  • Mobile transaction detail (fixed) — tapping a transaction on mobile was a dead no-op (FinanceScreen.kt called the no-op onOpenTransactionDetail and short-circuited the edit fallback). The shared navhost now wires onOpenTransactionDetail → a new shared TransactionDetailRoute → a new shared TransactionDetailDestination that renders the existing commonMain TransactionDetailScreen (via the shared TransactionViewModel) and hosts the edit form (TransactionForm) so Edit works without a dedicated route. Desktop unchanged.
  • Chat tier-gate on mobile (fixed) — mobile now gates Screen.Chat.route on AuthState.Authenticated.tier, matching desktop: FREE users see a “premium feature” upsell instead of the assistant.
  • Mobile timer list-detail (fixed, slot-based) — TimerControls + TimerListDetailScreen moved to commonMain; the add/edit-timer dialog is now a slot so desktop keeps its rich TimersDialog (which depends on desktop-only duration utils — never ported) and mobile supplies a compact MobileTimerDialog. The screen is now responsive (stacks on narrow widths). New shared TimerListDetailRoute + TimerListDetailMobileDestination (adapts the shared TimersViewModel’s dashboard state into a TimerPlaybackState; live websocket notifications are desktop-only for now). The shared TimersViewModel gained timerDetailList + a public loadTimerListByID. TimersScreen.onOpenTimerList is now wired on mobile.

Verdict: all four asymmetries from this audit are now closed and compile-verified (Principles-on-Web, mobile transaction detail, mobile timer list-detail, chat tier-gate). The shared & desktop stacks are at functional parity; remaining differences (More menu, feature-flag guards, walkthrough, live timer-notification panel) are intentional or platform-shell.

2026-06-11 — post-parity feature round (four prototype features):

  • Nutrition skip-with-alternative — NutritionViewModel.skipMeal() over the existing trackRecipeSkippedWithAlternative endpoint; a “Skip” action on today’s meal rows opens SkipMealDialog (pick a library recipe, or enter a manual name + macros via AlternativeNutrients).
  • Statistics focus/work time — ActivityBucketDTO/ActivityBucket gained focusMinutes/workMinutes; the server StatisticsService now sums durationMinutes per bucket from StudySessions (focus) and WorkSessions (work), rendered as a “Time spent” bar panel in StatisticsScreen.
  • Habit per-day history — new GET /habits/{id}/history?startDate=&endDate= (HabitService.getCheckInHistory over HabitTracks → HabitHistoryDTO); client HabitService.getHabitHistory + HabitsViewModel.loadHabitHistory feed a 30-day check-in heatmap on HabitDetailScreen (both stacks).
  • Search detail screens — new shared Book/Recipe/StudyItem/JournalDetailScreens (read-only, fetch by id via the existing services; journal reuses the pre-existing GET /daily-journals/{id} with a new client getJournalById). Routed on both navhosts (*DetailRoute, desktop mirrors with local route copies); onOpenResult now deep-links book/recipe/study/journal results, not just task/habit.
  • Search quality — server SearchService now splits the query into terms and requires all to match (AND) across each type’s columns, then ranks results (exact title → prefix → contains → shorter); a true Postgres tsvector/GIN index remains a future optimization.
  • Nutrition barcode scan — server OpenFoodFactsService proxies the free Open Food Facts API (GET /nutrition/barcode/{code} → clean BarcodeProductDTO, sidestepping client JSON-engine differences); client RecipesService.lookupBarcode + NutritionViewModel.lookupBarcode + a “Scan” action on the Library tab open BarcodeLookupDialog, then prefill AddEditRecipeDialog. (Camera capture is out of scope — the user types/pastes the barcode.)
  • Still open: onboarding per-day prompt (no prototype/code reference — needs a placement + content spec before building), camera-based barcode capture, and the tsvector/GIN search index.

2026-06-11 — parity round closed the last mobile↔desktop asymmetries:

  • Mobile Task power-features — the shared TasksViewModel was enriched (in-memory viewMode/sort/overdue/undated flows + previewTaskImport/importTasksFromText/previewTaskTagAi/applyTaskTagAi wrappers over the commonMain TaskService). The shared TasksScreen mobile overload now wires view modes (incl. the Eisenhower matrix), AI import (TaskImportSheet), and AI tag-organize (AiTagOrganizerContent), reached from a TasksMobileSettingsPanel. (showInListFrom already flowed via NewEditTaskMobileSheet.)
  • Desktop Habit detail — new HabitDetailDestination + composable<HabitDetailRoute> in the desktop navhost render the shared HabitDetailScreen (backed by the shared services.habits.HabitsViewModel, registered as a single in desktop Koin). Tapping a habit now opens the detail screen instead of a no-op self-nav.
  • Search deep-linking — SearchScreen gained onOpenResult; both navhosts route task/habit results to TaskDetailRoute/HabitDetailRoute, other types fall back to the module via the now-public routeForType.
  • Nutrition recipe CRUD — NutritionViewModel gained createRecipe/updateRecipe/deleteRecipe over the existing RecipesService endpoints; the Library tab has an Add-recipe action and tappable cards opening AddEditRecipeDialog (name · meal-tag · macros · note · delete). Barcode/image “scan” still pending.
  • Desktop bank-statement picker — BankStatementFilePicker.jvm.kt now uses a real JFileChooser (see above).
  • Statistics range switcher — verified already fully wired (Day/Week/Month/Year); no change needed.

None block the core experience.

Desktop parity is now closed — Command / Search / Focus / Onboarding routes are registered (with first-launch onboarding gating), Statistics & Nutrition render the shared prototype rebuilds, and the Work role-routes (WorkTimeLog, WorkStats, WorkTickets/Clients/Store) are wired. iOS finance pickers and the desktop notifications topbar dropdown are done. The genuinely-remaining work, classified by where the gap actually is:

  • Work depth — largely done on the shared stack:

    • New GET /work/jobs/{jobId}/store-pnl/lines (WorkStoreService.getStorePnLLines over the job’s Transactions) + client WorkStorePnLLine / WorkService.getStorePnLLines(), surfaced as an expandable P&L line-item detail in WorkStoreScreen.
    • Inventory stock-events sparkline — WorkService.getStoreStockEvents() + a running-level StockSparkline per product, lazy-loaded by WorkStoreViewModel.loadStockEvents.
    • Create dialogs (shared WorkFormDialogs primitives — WorkFormDialog/WorkTextField/WorkOptionRow): supplier, product, stock-event, shift (store; shift uses a people picker now that WorkStoreViewModel loads getPeople); ticket + person (SE); client + project + milestone (CS). Each backed by new *ViewModel.create* methods over the existing WorkService routes.
    • SE tabs: WorkTicketsScreen now has Overview (KPI strip + by-status breakdown bars), Board (the kanban), and Team (people list + add-person) tabs.
    • CS project detail: project rows in WorkClientsScreen expand to show milestones (billing/delivery breakdown, lazy-loaded) with an add-milestone dialog.
    • Live running-timer (done): WorkSessionTimerRepository is now registered as a single in all four platform DI modules (TimerWebSocketClient + TimerService + TokenStorage were already registered everywhere). WorkTimeLogViewModel gained startTimedSession / stopActiveTimer — it opens a session with actualStart, kicks the server timer via the repo (best-effort), runs a local stopwatch so the UI ticks even if the WebSocket is down, then completeSession on stop. WorkTimeLogScreen shows a Start timer action in the session dialog and a running-timer banner (elapsed hh:mm:ss + Stop).

    Work depth is now complete on the shared stack.

  • Statistics (done): new dedicated GET /statistics?range= endpoint (StatisticsService + statisticsRouting, leaving /dashboard untouched) with real per-bucket activity aggregation over Day/Week/Month/Year and prior-period task/workout deltas; streaks/net-cash/completion come from the dashboard slice. Client StatisticsService + StatisticsViewModel + a range segmented control in StatisticsScreen, registered in all four platform DI modules.

  • Nutrition: per-user macro goals are now implemented — added daily{Calorie,Protein,Carbs,Fat,Fiber,Sugar}Goal columns to UserSettings (exposed via the existing /settings GET/PUT), NutritionViewModel reads them (prototype defaults as fallback) and an in-app Daily macro goals dialog persists edits. Remaining (frontend-feasible): recipe-CRUD / add-food / skip-with-alternative sheets (POST/PATCH/DELETE /nutrition/recipes already exist).

  • Search (done): GET /search aggregates across tasks/habits/journal/books/recipes/study (SearchService + searchRouting); client SearchService + SearchViewModel drive the live results in SearchScreen (debounced), registered in all four platform DI modules. Result deep-linking to the specific record is now implemented for all types (see the 2026-06-11 feature round above); only full-text/FTS ranking remains.

  • Tasks & Habits mobile parity (done): the shared editors went from name+priority / name+frequency to full field parity with desktop — note, due/scheduled date+time (real CustomDatePicker/CustomTimePicker), subtasks, reminders, and (tasks) tags + project pickers — plus dedicated Task/Habit detail screens (TaskDetailRoute / HabitDetailRoute) with complete/check-in, edit and delete. TaskService.addTask was extended to carry reminders+tags on create; ProjectApiService was registered in the Android/iOS DI modules (was desktop/web-only — the omission only surfaced once the mobile editor injected ProjectsViewModel). Remaining task-only desktop extras on mobile: showInListFrom, AI import / AI tag organize, Eisenhower-matrix view modes.

Verification

./gradlew :shared:compileKotlinJvm :composeApp:compileAndroidMain \ :composeApp:compileDevelopmentExecutableKotlinWasmJs \ :composeApp:compileKotlinDesktop :server:compileKotlin bash scripts/check_design_tokens.sh --strict