Skip to content

feat(core): B4 core/network + B6 core/database structural migration (store5-adoption)#2691

Draft
therajanmaurya wants to merge 14 commits into
openMF:store5Migrationfrom
therajanmaurya:feat/store5-full-migration
Draft

feat(core): B4 core/network + B6 core/database structural migration (store5-adoption)#2691
therajanmaurya wants to merge 14 commits into
openMF:store5Migrationfrom
therajanmaurya:feat/store5-full-migration

Conversation

@therajanmaurya

Copy link
Copy Markdown
Member

Summary

Phase B of the store5-adoption epic — full structural migration of core/network (B4) and core/database (B6) to template-aligned layout, plus a 7-wave quality pass (B6.8–B6.14).

  • B4 — 21 per-domain <d>/api/<D>Api.kt files replacing legacy services/ + datamanager/ + apis/ + BaseApiManager + FlowConverterFactory. MifosApiClient central wrapper. Template-aligned httpClient() + Mifos Fineract-Platform-TenantId defaultRequest.
  • B6 — Per-feature {entity, dao, mapper, converter}/ for 14 features + 4 shared. All legacy entities/, dao/, helper/ deleted. core/database shrinks ~310 → ~150 files (−52%).
  • B6.8–B6.14 quality passGetCurrentTimeInMilliskotlin.time.Clock; 4 pass-through DAO helpers deleted; 41 plain DTOs moved core/databasecore/model/objects/; CodeValue generic collapses 16 recurringdeposit lookups; @Parcelize stripped (290 files); recurringdeposit/ deleted from core/database (was 100% pure DTOs).

Zero @Deprecated. Zero legacy room.entities.* / room.dao.* / room.helper.* paths. Zero @Parcelize in core/database + core/model.

Detailed breakdown

B4 — core/network

Item Status
21 per-domain Apis (auth/center/charge/checkerinbox/client/collectionsheet/datatable/document/fixeddeposit/group/loan/note/office/pathtracking/recurringdeposit/report/savings/search/share/staff/survey)
MifosApiClient central wrapper at core/network/mifosclient/
Template-aligned httpClient() + setupDefaultHttpClient(...)
Legacy services/, datamanager/, apis/, BaseApiManager, FlowConverterFactory, FlowConverter deleted outright (F29 zero-deprecation)
core/network/build.gradle.kts + ProGuard rules cleaned
Companion mapping doc — active/core-network/API_MAPPING.md (156 rows × 21 domains)

B6 — core/database per-feature relocation

14 features + 4 shared folders under com.mifos.room.*:

  • center (8) / charge (8) / client (19) / collectionsheet (3) / datatable (5) / group (9) / loan (21) / note (4) / office (6) / pathtracking (3) / savings (22) / staff (4) / survey (7) + di / infra / typeconverters / utils
  • APIEndPoint.kt moved cross-tier basemodel/core/network
  • Timeline.kt moved cross-tier room.entities.Timelinecore.model.objects.timeline.Timeline (8 importers rewritten)
  • All legacy entities/, entities/noncore/, entities/zipmodels/, dao/, helper/ deleted

B6.8–B6.14 — quality pass

Wave Outcome
B6.8 6 GetCurrentTimeInMillis platform files deleted; LoanDaoHelperkotlin.time.Clock
B6.9 4 pass-through DAO helpers deleted (Office/Staff/Survey/Charge); DI bindings removed
B6.10 41 plain DTOs core/databasecore/model/objects/<f>/; recurringdeposit/ deleted from core/database; 18 transitively-Room-dep files stay (tracked for B5 split)
B6.11 CodeValue generic at core/model/objects/common/; 16 lookup typealiases in RecurringDepositLookups.kt (consumer imports preserved)
B6.12 NoteEntity transitional state tracked in PLAN Part 11 (Phase C feature/note rewrite deletes legacy Note table)
B6.13 @Parcelize + Parcelable stripped — core/database 60→0, core/model 230→0
B6.14 5 orchestration DAO helpers (Center/Client/Group/Loan/Savings) tracked for B5 Repository fold-in (no in-code markers per user direction)

Deliverables

  • AppStoreRegistry.Ttl durations object (Short=5m / Medium=30m / Long=1h / Day=1d) for Phase C Store providers
  • CodeValue generic at core/model/objects/common/CodeValue.kt
  • RecurringDepositLookups.kt — 16 typealiases preserving consumer import paths
  • Companion docs in plan-layer (framework repo, separate PR):
    • active/core-network/API_MAPPING.md
    • active/core-database/ENTITY_MAPPING.md
    • active/core-database/PLAN.md (Part 11 — cross-module migration handoff/deferral registry)
    • active/core-database/QUALITY_AUDIT.md

Net file delta

  • −85 files (6 utils + 4 helpers + 59 misplaced data classes — net 41 moved + 18 reverted/16 collapsed)
  • +4 files (CodeValue.kt + RecurringDepositLookups.kt + companion plan docs in framework repo)
  • ~290 annotation strips (@Parcelize + Parcelable)
  • ~250 import rewrites project-wide

Test plan

  • ./gradlew :core:network:assembleDebug --dry-run green
  • ./gradlew :core:database:assembleDebug --dry-run green
  • ./gradlew :core:model:assembleDebug --dry-run green
  • No @Deprecated survivors: grep -rn "@Deprecated" --include='*.kt' core/database/ returns 0
  • No legacy path survivors: grep -rn "com\.mifos\.room\.entities\.\|com\.mifos\.room\.dao\.\|com\.mifos\.room\.helper\." --include='*.kt' . returns 0
  • No @Parcelize in core/database + core/model: grep -rln "@Parcelize" --include='*.kt' core/database core/model returns 0 (excluding the framework Parcel def itself)
  • core/data consumers fail to compile (expected — B5 next applies the mappings)

Follow-ups (out of scope here)

Tracked in active/core-database/PLAN.md Part 11 (deferral registry):

  • B5 — rewrite ~68 core/data/repositoryImp/*.kt against new Apis + per-feature paths
  • B5 — Entity↔Domain split for 18 plain data classes that transitively import Room entities
  • B5 — fold 5 orchestration DAO helpers into Repository Store.SourceOfTruth.of(...) blocks; delete the helpers
  • Phase D — collapse 3 Room @Entity lookups (LoanType, ChargeTimeType, ChargeCalculationType) into shared code_value(domain, id, code, value) Room table with MIGRATION_3_4
  • Phase C feature/note — rewrite Create/Edit Note via NoteListStore; delete NoteEntity + Note table + NoteEntityMapper
  • B5 — drop kotlin.parcelize gradle plugin from core/database/build.gradle.kts + core/model/build.gradle.kts (no-op now, but cleanup)

This PR keeps draft status until B5 lands the consumer rewrites, so it can be reviewed as a single end-to-end migration unit on the store5Migration accumulation branch.

Rajan Maurya added 4 commits May 22, 2026 00:09
…ith kmp-project-template

Lands the structural infrastructure waves that don't depend on per-feature migration.
Consolidates the core/* infra layer to match kmp-project-template's structure end-to-end,
keeping legacy code in place where Phase C feature waves will drain it.

This is the first half of the single mega-PR scope (F24); per-feature B4/B5/B6 + Phase C
+ Phase D follow on the same branch.

## Layers landed

### Phase B3 — core/database structural fix

- Single commonMain `MifosDatabase` declaration (`@Database(entities=[...]) @ConstructedBy(MifosDatabaseConstructor::class)` + `expect object MifosDatabaseConstructor : RoomDatabaseConstructor<MifosDatabase>`)
- Deleted 3 platform actuals (androidMain/desktopMain/nativeMain MifosDatabase.kt with duplicated `@Database`)
- `exportSchema = true` (replaces v2's `false` — enables future `@AutoMigration` annotations on v3→v4+)
- Manual `MIGRATION_1_2` + `MIGRATION_2_3` consolidated to commonMain (no longer duplicated in per-platform DI)
- Per-platform `DatabaseModule.<platform>.kt` (5 platforms — Android, Desktop, Native, JS, WasmJS) now provide ONLY the `AppDatabaseFactory.createDatabase<MifosDatabase>(...).build()` block — no more `MIGRATION_*` constants
- JS + WasmJS `DatabaseModule` files now properly build the DB (matching template; field-officer's were empty stubs before — that was a bug)
- New commonMain `DatabaseModule.kt` with DAO singles + `expect val platformModule: Module`
- `DATABASE_NAME = "mifos_field_officer.db"` moved from `core.common.utils.Constants` to `MifosDatabase.companion` — app-branded
- Legacy `DaoModule.kt` + `DatabaseModule.common.kt` deleted; KoinModules.kt updated

### Phase B6b — core/data/util extensions

- New `core/data/util/ResultExtensions.kt` — bridges `NetworkResult<D, *> ↔ DataState<D>` via the `template.core.base.network.*` types
- New `core/data/util/SharedFlowExtensions.kt` — generic `bufferedMutableSharedFlow<T>(replay)` helper

### Phase B6c — TimeZoneMonitor + nonAndroidMain source set

- New `core/data/infra/TimeZoneMonitor.kt` interface (commonMain)
- New `core/data/androidMain/.../infra/impl/TimeZoneMonitorImpl.kt` — BroadcastReceiver-based, listens for `ACTION_TIMEZONE_CHANGED`, uses `DispatcherManager` for IO + appScope
- New `core/data/nonAndroidMain/.../di/TimeZoneMonitorImpl.kt` — simple `flowOf(TimeZone.currentSystemDefault())` for Desktop/Native/JS/WasmJS
- New `core/data/nonAndroidMain/.../di/PlatformModule.kt` — `actual val platformModule` binding `TimeZoneMonitor`
- Updated `core/data/androidMain/.../di/PlatformModule.android.kt` — Android `actual val platformModule` binds `TimeZoneMonitor` with Context + DispatcherManager
- Deleted the 4 per-platform empty `PlatformModule.<platform>.kt` files (Desktop/Native/JS/WasmJS) — consolidated into the `nonAndroidMain` source set matching template

### Phase B7 — core/analytics module

- New `:core:analytics` module added to settings.gradle.kts
- 5 files: `MifosAnalyticsTracker`, `MifosAnalyticsEvents`, `MifosAnalyticsExtensions`, `MifosComposeAnalytics`, `AnalyticsUsageExamples` — copied from template, package adapted `org.mifos → com.mifos`, license URL updated, copyright year 2026
- `analyticsModule` from `template.core.base.analytics.di` now included in `cmp-navigation/KoinModules.allModules`

### Phase B8 — core/ui template-aligned primitives

- 9 new files in `core/ui`:
  - `NavigationItem.kt`
  - `bottombar/{KptBottomBar, KptNavigationBarItem, KptNavigationRail, KptNavigationRailItem}.kt`
  - `input/{PasswordStrengthIndicator, RevealSwipe}.kt`
  - `scaffold/{KptScaffold, KptPullToRefreshState}.kt`
- 3 files skipped (V3 duplicate): `utils/PasswordChecker`, `utils/PasswordStrength`, `utils/PasswordStrengthExtensions` already exist at `util/` (singular) in field-officer
- `core/ui/build.gradle.kts` added `api(projects.coreBase.store)` so `KptPullToRefreshState` can reach `ScreenDataStream` / `PagingScreenStream`

### cmp-* alignment

- `cmp-navigation/AppViewModel.kt` (renamed from `ComposeAppViewModel.kt`):
  - `AppState` adds `isScreenCaptureAllowed: Boolean = true`
  - `AppEvent` adds `data object Recreate : AppEvent`
- `cmp-navigation/ComposeApp.kt` signature aligned with template:
  - Added `updateScreenCapture: (Boolean) -> Unit` + `handleRecreate: () -> Unit` params
  - Wired `LaunchedEffect(uiState.isScreenCaptureAllowed) { updateScreenCapture(...) }`
  - Wired `EventsEffect ... is AppEvent.Recreate -> handleRecreate()`
  - This fixes a pre-existing compile error — `SharedApp` was already passing these 2 callbacks to `ComposeApp` which didn't accept them
- `cmp-navigation/di/KoinModules.kt`:
  - All `ComposeAppViewModel` references renamed to `AppViewModel`
  - Added `template.core.base.analytics.di.analyticsModule` to `allModules`
  - Added `template.core.base.security.di.SecurityModule` to `allModules`
- `cmp-navigation/build.gradle.kts` — added `implementation(projects.coreBase.security)` (SecurityModule was not on classpath transitively)

## What's deferred

- **B4** (core/network per-feature APIs for 14 remaining features) — per-feature work that fits Phase C waves naturally
- **B5** (core/database per-feature `<feature>/{converter, dao, entity, mapper}/` relocation) — per-feature
- **B6** (core/data per-feature Repositories + Stores) — per-feature
- **B10/B11/B12** (collapse legacy `DataState` / use cases / `objects/<flat>` models) — these are DELETE work that requires consumers to be migrated first; folded into per-feature waves + Phase D
- **Phase C waves 5-18** (feature/* modernization for 14 remaining features) — feature-by-feature
- **Phase D** orphan deletion + final cleanup

## V1/V2/V3 protocol applied (F25)

Per the verification HARD RULE pinned this session, every file copied from template was verified against field-officer's actual surface BEFORE writing. Notable:

- `core/data/user/UserDataRepository` + impl + LogoutEvent + LogoutReason — REFUSED to copy (V3 failure — field-officer's `UserPreferencesRepository` already covers the domain; would have produced ~18 broken method-references)
- 3 `core/ui/utils/Password*.kt` files — REFUSED to copy (V3 failure — equivalents exist at `core/ui/util/` singular)
- `ComposeApp` signature alignment — proceeded after V1 confirmed `AppState.isScreenCaptureAllowed` + `AppEvent.Recreate` could be added safely

All decisions logged in `plan-layer/.../store5-adoption/CORE_MODULES_AUDIT.md` "Decisions logged so far" table.
…nd-to-end migration

Three feature waves end-to-end to match kmp-project-template architecture.
Each feature got full vertical-slice migration: core/network per-feature API,
core/data per-feature Repository (no Store5 — documented per-wave exception),
feature/<name> restructured to {ui, di, navigation}/ with BaseViewModel<S,E,A>
+ ScreenContent / MutationScreenContent, composeResources audit, pure-delegator
use cases dropped.

## Wave 7 — feature/note

- core/network/note/api/NoteApi.kt — modern suspend Ktorfit (5 endpoints)
- core/data/note/{NoteRepository, impl/NoteRepositoryImpl} — relocated from legacy repository/repositoryImp; no DataState/withNetworkCheck/runCatching
- feature/note restructured to {ui, di, navigation}/ — both NoteViewModel (list) + AddEditNoteViewModel use BaseViewModel + SubmitHandler; State split into separate files; Screens use ScreenContent + MutationScreenContent
- Deleted 3 pure-delegator use cases (AddNote, UpdateNote, DeleteNote) + UseCaseModule entries
- @deprecated legacy NoteService methods
- consumer imports updated (cmp-navigation + feature/client)
- composeResources: 5 new strings, orphan strings flagged for Phase D
- gradle: dropped projects.core.domain; only projects.core.{common,model,data,ui}
- Documented RULE-STORE5-FETCH-001 exception (notes are resource-scoped CRUD, no Store5 needed)

## Wave 8 — feature/path-tracking

- core/network/pathtracking/api/PathTrackingApi.kt — suspend Ktorfit (GET list + POST add)
- core/data/pathtracking/{PathTrackingRepository, impl/PathTrackingRepositoryImpl} — relocated
- feature/path-tracking restructured to {ui, di, navigation}/ — VM extends BaseViewModel; ScreenState<List<UserLocation>>; State + Event + Action split
- 5 per-platform Screen actuals kept (Android Google Maps + BroadcastReceiver; desktop/native/js/wasmJs now render shared list — was TODO previously)
- Deleted GetUserPathTrackingUseCase + UseCaseModule entry
- @deprecated legacy via DataManagerDataTable / DataTableService chain (left for Phase D)
- composeResources audit
- Documented RULE-STORE5-FETCH-001 exception (officer-scoped low-value list, pull-to-refresh)

## Wave 9 — feature/document

- core/network/document/api/DocumentApi.kt — suspend Ktorfit with @multipart upload
- core/data/document/{DocumentRepository, impl/DocumentRepositoryImpl} — relocated
- feature/document restructured to {ui, di, navigation}/ — DocumentListViewModel (list) + UploadDocumentViewModel (dialog) + Screens with MutationScreenContent
- @deprecated legacy DocumentService methods (5)
- 6 use cases left in core/domain for now (consumed by feature/client modules — Phase D deletes once client migrates)
- composeResources audit
- gradle: drops projects.core.domain (no surviving use cases in feature/document); drops kotlin-parcelize plugin
- Documented Phase D follow-up: feature/client still imports legacy DocumentListRepository + DocumentCreateUpdateRepository + 3 use cases — will be cleaned when client migrates

## Phase D stragglers added

- core/data/util/NetworkExtensions.kt — withNetworkCheck() has zero live consumers after Wave 7; ready for Phase D delete
- core/network/services/{NoteService, RunReportsService, ...}.kt — @deprecated, ready for Phase D delete once consumers migrate
- core/network/datamanager/{DataManagerNote, ...}.kt — same

## Constraints applied

- RULE-NO-RUN-CATCHING-001 — zero runCatching/Result<T> in production
- RULE-NO-HARDCODED-STRINGS — all user-facing strings from composeResources
- RULE-FEATURE-USES-ONLY-CORE — feature gradle deps only projects.core.*
- RULE-STORE5-FETCH-001 — documented exception per feature
- F25 V1/V2/V3 verification — types verified before write; some files refused/skipped
…H-001)

The initial Wave 7 (note) shipped without Store5 — repository returned suspend
List<Note> with manual try/catch in the VM. The user flagged: "No migration done
with fully store5". This commit fixes it by adding the full Store5 stack for
note as the canonical reference all subsequent waves must follow.

## What was wrong before

- core/data/note/NoteRepository — exposed `suspend fun listNotes(...): List<Note>`
- core/data/note/impl/NoteRepositoryImpl — direct ktorfit call, no cache
- feature/note/ui/NoteViewModel — manual `try/catch + ScreenState.Error` block
- NO cache table, NO Store, NO ScreenDataStream
- Used the "documented exception" path which doesn't apply to remote fetches

## What this commit fixes — proper Store5 wiring

### core/database (cache layer)

- `core/database/.../note/entity/NoteCacheEntity.kt` — Room entity with composite
  cacheKey + resourceType + resourceId + noteId + payload columns (note text,
  createdBy*, createdOn, updatedBy*, updatedOn)
- `core/database/.../note/dao/NoteCacheDao.kt` — DAO with upsertAll +
  observeByResource (Flow<List<NoteCacheEntity>>) + deleteByResource + deleteByKey
- `core/database/.../note/mapper/NoteEntityMapper.kt` — Note ↔ NoteCacheEntity
- `MifosDatabase` bumped v3 → v4; new `note_cache` table registered in
  @database(entities=[...]) + new `noteCacheDao` abstract val
- `MIGRATION_3_4` raw SQL added to MifosDatabaseMigrations array
- `DatabaseModule.kt` exposes `single { get<MifosDatabase>().noteCacheDao }`

### core/data (Store + Repository)

- `core/data/.../note/store/NoteListKey.kt` — @serializable composite key
  (resourceType, resourceId) + cacheKey() extension for FetchedAtRepository
- `core/data/.../note/impl/NoteListStore.kt` — `provideNoteListStore(api,
  networkMonitor, dao)` matching template's `provideCoinDetailStore` pattern.
  Fetcher uses `executeWithRetry` from cmp-network-monitor. SourceOfTruth reader
  observes Room DAO Flow; writer deletes-then-upserts (lists refresh wholesale).
- `core/data/.../note/NoteRepository.kt` — interface now has
  `fun notesStream(keyFlow, scope): ScreenDataStream<List<Note>>` (read flow)
  alongside the existing suspend mutations
- `core/data/.../note/impl/NoteRepositoryImpl.kt` — constructor injects the Store
  + NetworkMonitor + FetchedAtRepository; `notesStream` delegates to
  `store.asScreenStream(...)`; mutations call private `invalidateList()` which
  `store.fresh(key)`s the cache after a successful write so subscribers see the
  new list automatically
- `RepositoryModule.kt` registers `provideNoteListStore(get(), get(), get())` as
  `single { Store<NoteListKey, List<Note>> }` and binds NoteRepository with
  explicit 4-param constructor

### feature/note

- `NoteState.kt` — dropped `screenState: ScreenState<List<Note>>` + `isRefreshing`
  fields (they're now driven by `viewModel.screenState` StateFlow + Store5
  `freshness` field); kept resourceId/resourceType + delete-dialog + expanded-row
  UI state
- `NoteViewModel.kt` — full rewrite: holds `keyFlow: MutableStateFlow<NoteListKey>`,
  creates `notesStream = repository.notesStream(keyFlow, viewModelScope)`,
  exposes `screenState: StateFlow<ScreenState<List<Note>>>` mapping empty
  Content → Empty; `OnRetry` / `OnRefresh` actions delegate to `notesStream.retry()`
  / `notesStream.refresh()`; delete relies on repository's `store.fresh()` to
  propagate via the stream (no manual reload)
- `NoteScreen.kt` — now collects both `state` + `screenState` from VM; computes
  `isRefreshing` from `freshness == UPDATING`; `NoteScreenContent` takes
  `screenState` as a separate param

## Why this is the canonical reference

This wave now matches template's `CoinDetailStore` + `CryptoRepositoryImpl`
pattern exactly. Every remaining feature wave (path-tracking, document, settings,
report, data-table, recurringDeposit, checker-inbox-task, center, groups, savings,
loan, client, offline, collectionSheet) MUST follow this same template:

1. Cache entity + DAO + mapper in `core/database/<feature>/`
2. @serializable Key + provideXxxStore function in `core/data/<feature>/`
3. DB version bump + migration adding the cache table
4. Repository exposes `xxxStream(keyFlow, scope): ScreenDataStream<X>`
5. RepositoryImpl uses `store.asScreenStream` + invalidates on mutations
6. VM consumes `notesStream.state` directly; no manual try/catch around reads
7. Screen reads ScreenState from VM, computes refresh-indicator from freshness

## Path-tracking + document — same fix still needed

Path-tracking + document were migrated in Waves 8 + 9 WITHOUT Store5 — they
also expose suspend repository methods with manual try/catch in VMs. They need
the same 7-step fix above to be RULE-STORE5-FETCH-001-compliant. Flagged as
follow-up.

## Outstanding work

This commit only fixes ONE feature. The full epic is far from complete:
- path-tracking + document need Store5 fix (same pattern)
- 10+ features still need initial Store5 migration (settings, report, data-table,
  recurringDeposit, checker-inbox-task, center, groups, savings, loan, client,
  offline, collectionSheet)
- Phase D orphan deletion pending
- App compile not validated (local gradle env unable)
…store5-adoption)

B4 — core/network: full template-aligned migration. 21 per-domain Apis at
`<d>/api/<D>Api.kt` (auth, center, charge, checkerinbox, client, collectionsheet,
datatable, document, fixeddeposit, group, loan, note, office, pathtracking,
recurringdeposit, report, savings, search, share, staff, survey) replacing legacy
services/, datamanager/, apis/, BaseApiManager, FlowConverterFactory, FlowConverter
(all deleted outright per F29 zero-deprecation). MifosApiClient central wrapper at
`core/network/mifosclient/` with 21 lazy Api accessors. Template-aligned
httpClient() + setupDefaultHttpClient(...) + Mifos Fineract-Platform-TenantId
defaultRequest. core/network/build.gradle.kts + ProGuard rules cleaned. Companion
mapping doc at active/core-network/API_MAPPING.md (156 rows × 21 domains, 68-file
B5 punch list).

B6 — core/database: full per-feature relocation. {entity, dao, mapper, converter}/
structure for 14 features (center 8 / charge 8 / client 19 / collectionsheet 3 /
datatable 5 / group 9 / loan 21 / note 4 / office 6 / pathtracking 3 / savings 22 /
staff 4 / survey 7) + 4 shared (di, infra, typeconverters, utils). Legacy entities/,
entities/noncore/, entities/zipmodels/, dao/, helper/ all deleted outright. APIEndPoint
moved cross-tier basemodel/ → core/network. Timeline cross-tier moved to
core.model.objects.timeline (8 importers rewritten). MifosDatabase.kt entity/DAO
import block rewritten to new per-feature paths.

B6.8–B6.14 quality pass (post-audit per user directives):
  - B6.8: GetCurrentTimeInMillis expect/actual deleted (6 platform files); LoanDaoHelper
    rewired to kotlin.time.Clock.System.now().toEpochMilliseconds().
  - B6.9: 4 pass-through DAO helpers deleted (OfficeDaoHelper, StaffDaoHelper,
    SurveyDaoHelper, ChargeDaoHelper); DI bindings removed from room/di/HelperModule.kt.
    5 orchestration helpers (Center/Client/Group/Loan/Savings) stay until B5 fold-in
    into Repository Store5 SourceOfTruth blocks.
  - B6.10: 41 plain data classes moved core/database → core/model/objects/<f>/;
    recurringdeposit/ + collectionsheet/ entity folders deleted from core/database
    (were 100% pure DTOs). 18 plain data classes stay in core/database (transitively
    import Room @entity types — circular-dep blocker; tracked for B5 Entity↔Domain
    split).
  - B6.11: CodeValue generic at core/model/objects/common/CodeValue.kt; 16 recurringdeposit
    lookups collapsed via typealiases in RecurringDepositLookups.kt (consumer imports
    preserved — typealiases live in same package).
  - B6.12: NoteEntity transitional state tracked in PLAN Part 11 (Phase C feature/note
    rewrite deletes NoteEntity + Note table + NoteEntityMapper).
  - B6.13: @parcelize + Parcelable stripped from core/database (60 → 0 files) +
    core/model (230 → 0 files). kotlin.parcelize gradle plugin removal deferred to B5.
  - B6.14: 5 orchestration DAO helpers tracked in PLAN Part 11 for B5 fold-in (no
    in-code markers per user direction — production code stays clean).

AppStoreRegistry.Ttl durations object (Short=5m / Medium=30m / Long=1h / Day=1d)
added to core/store/AppStoreRegistry.kt for Phase C Store providers.
StoreCacheManager parity verified — already on new per-feature paths + kotlin.time.Clock.

Net: −85 files / +4 (CodeValue + RecurringDepositLookups + API_MAPPING + ENTITY_MAPPING) /
~290 annotation strips / ~250 import rewrites. core/database: ~310 → ~150 files (−52%).
Zero @deprecated, zero room.entities.*/room.dao.*/room.helper.* legacy paths, zero
@parcelize in core/database + core/model.

Mapping handoff for B5 (core/data, 68 broken consumers):
  - active/core-network/API_MAPPING.md — 156 legacy→new method-grep rows
  - active/core-database/ENTITY_MAPPING.md — 24 universal rules + ~138 type rewrites
  - active/core-database/PLAN.md Part 11 — deferral registry (Phase D schema collapses;
    B5 Repository fold-ins; 18 file Entity↔Domain splits)
  - active/core-database/QUALITY_AUDIT.md — full B6.8–B6.14 retrospective
@coderabbitai

coderabbitai Bot commented May 22, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ee912682-5148-41d8-a7f0-5c73caeb70f9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Rajan Maurya added 2 commits May 22, 2026 17:27
… + toolchain bump + StoreCacheManager move + iosX64 drop

Adopts the 3 merged kmp-project-template PRs into field-officer:
  - openMF/kmp-project-template#160 — Room 3 alpha05 + AppStoreRegistry consolidation + iosX64 drop
  - openMF/kmp-project-template#161 — ChargeTypeConverters commonMain install (N/A: field-officer's converter is empty stub)
  - openMF/kmp-project-template#162 — detekt MatchingDeclarationName follow-up to openMF#161 (N/A: same)

TS-1 — Room 3 alpha05 + toolchain bump
  - gradle/libs.versions.toml: room 2.8.4 → 3.0.0-alpha05; artifact rename androidx.room:room-* → androidx.room3:room3-* (compiler/runtime/ktx/testing/paging/gradle-plugin); plugin id androidx.room → androidx.room3
  - gradle/libs.versions.toml: kotlin 2.2.21 → 2.3.20; ksp 2.2.21-2.0.4 → 2.3.6; compose-plugin 1.9.3 → 1.10.3; uiBackhandler 1.9.3 → 1.10.3; ktorfit 2.7.1 → 2.7.3
  - build-logic/convention/.../KMPRoomConventionPlugin.kt: drop kspIosX64 from KSP target list (Room 3 alpha05 no longer publishes iosX64 variants)
  - core/database/.../MifosDatabase.kt: add `override fun initialize(): MifosDatabase` to expect MifosDatabaseConstructor (Room alpha05 added new abstract method on RoomDatabaseConstructor)

TS-2 — Drop iosX64 KMP target
  - cmp-shared/build.gradle.kts: remove `iosX64()` from binaries.framework loop
  - build-logic/convention/.../org/convention/KotlinMultiplatform.kt: remove `iosX64()` from configureKotlinMultiplatform
  - build-logic/convention/.../org/mifos/KotlinMultiplatform.kt: remove commented-out iosX64() + explanatory comment
  - Apple Silicon devs use iosSimulatorArm64; Intel Macs are EOL

TS-3 — Move StoreCacheManager core/data/infra → core/store/infra
  - git mv core/data/.../infra/StoreCacheManager.kt → core/store/.../infra/StoreCacheManager.kt (history preserved)
  - git mv core/data/.../infra/impl/StoreCacheManagerImpl.kt → core/store/.../infra/impl/StoreCacheManagerImpl.kt
  - Package decls: com.mifos.core.data.infra(.impl) → com.mifos.core.store.infra(.impl)
  - 1 consumer import rewrite: core/data/.../di/RepositoryModule.kt
  - core/store/build.gradle.kts: add projects.core.database + kotlinx-coroutines-core + kotlinx-datetime + kermit-logging deps

TS-4 — Doc updates
  - active/feature-vertical-migration/PLAN.md: add Tier -1 (template adoption) above Tier 0; record TS-1..TS-4 all done; PR openMF#161/openMF#162 marked N/A (field-officer's ChargeTypeConverter is empty stub with no FieldEncryptor injection pattern to lift)

Verification gates (all PASS):
  - Zero `com.mifos.room.entities.*` legacy paths project-wide
  - Zero `com.mifos.core.data.infra.StoreCacheManager` references (all migrated to com.mifos.core.store.infra)
  - Zero active `iosX64()` declarations across build files
  - libs.versions.toml uses androidx.room3:room3-* artifact names + plugin id androidx.room3
  - MifosDatabaseConstructor has initialize() override required by Room 3 alpha05

Cross-references:
  - active/feature-vertical-migration/PLAN.md Tier -1 (this commit's executed tasks)
  - active/feature-vertical-migration/MIGRATION_PLAYBOOK.md (will accumulate per-feature wave learnings starting Tier 1)
  - active/core-database/PLAN.md (B6.1–B6.14 prior context)
…androidx-room-gradle-plugin aliases that build-logic referenced but libs.versions.toml didn't declare

build-logic/convention/build.gradle.kts referenced 4 libs aliases that did not exist
in libs.versions.toml — silent broken-ness that template's libs.versions.toml has
correct entries for. Aligning field-officer with template's alias names.

Added to libs.versions.toml:
  [versions]
    + kover = "0.9.1"
    + kmpProductFlavors = "2.4.2"
  [libraries]
    + androidx-room-gradle-plugin (same artifact as existing room-gradlePlugin; new
      alias because build-logic uses libs.androidx.room.gradle.plugin not libs.room.gradlePlugin)
    + kover-gradlePlugin = org.jetbrains.kotlinx:kover-gradle-plugin
    + kmp-product-flavors-plugin = io.github.mobilebytelabs.kmpflavors:flavor-plugin
  [plugins]
    + kover = org.jetbrains.kotlinx.kover
    + kover-convention = org.convention.kover.plugin
    + kmp-product-flavors = io.github.mobilebytelabs.kmp-product-flavors

Renamed (no consumer break — verified zero consumers of old name):
    spotless-gradlePlugin → spotless-gradle (matches template + build-logic ref)

cmp-desktop/build.gradle.kts:
  + alias(libs.plugins.kover.convention) — applies kover code-coverage explicitly.
    cmp-desktop doesn't apply a base convention plugin (KMPLibrary/AndroidApplication
    chain kover automatically; cmp-desktop uses raw kotlinMultiplatform + compose +
    serialization plugins so needs explicit application — same pattern as template
    post-PR openMF#160).

All 14 build-logic libs.* references now resolve. Other cmp-desktop diffs vs
template are project-specific (LICENCE British spelling, packageName key naming,
compose.components.resources dep) — kept as-is.
Rajan Maurya added 2 commits May 22, 2026 18:24
…+ gradle exclusion

Build-fix prerequisite for the feature-vertical migration (Tier 0 of
active/feature-vertical-migration/PLAN.md). After B4 (core/network) + B6
(core/database) shipped, 60 consumer files in core/data + feature/* still
referenced deleted `com.mifos.core.network.datamanager.*` types — silent
broken state that blocked any per-feature wave from starting compile-green.

This commit quarantines those 60 files into `legacy/` subfolders preserving
their relative source-set paths, configures gradle to exclude `**/legacy/**`
from compilation in the 3 affected modules, and slims `RepositoryModule.kt`
to only its still-live bindings. Each per-feature wave (Tier 1) re-adds its
slice of the broken bindings by copy-pasting from `legacy/RepositoryModule.legacy.kt`
and rewriting the impl against the new B4 Apis + B6 entity paths.

Net file changes (65 total):
  - 60 broken consumer files moved (git mv preserves history) into:
      core/data/src/commonMain/kotlin/com/mifos/core/data/legacy/{repositoryImp,pagingSource,syncsurvey/impl}/
      feature/checker-inbox-task/src/commonMain/kotlin/com/mifos/feature/checker/inbox/task/legacy/checkerInboxDialog/
      feature/settings/src/commonMain/kotlin/com/mifos/feature/settings/legacy/syncSurvey/
  - Package declarations rewritten in all 60 files:
      com.mifos.<...> → com.mifos.<...>.legacy.<...>
  - RepositoryModule.kt: 304 → 133 lines. Keeps Login, Search, ActivateRepository,
    DocumentRepository, NoteListStore + NoteRepository (Wave 7), PathTrackingListStore
    + PathTrackingRepository (Wave 11), SearchRecord (Wave 4), AppLock, Passcode,
    Biometric, UserVerification, and 6 still-live legacy Impls (GroupListRepositoryImp,
    GroupLoanAccountRepositoryImp, LoanAccountApprovalRepositoryImp, LoanChargeRepositoryImp,
    LoanRepaymentScheduleRepositoryImp, LoanTransactionsRepositoryImp) whose impls
    didn't import DataManager*. Removed 55 broken bindings.
  - NEW core/data/.../legacy/di/RepositoryModule.legacy.kt — 223 lines, entire content
    is a Kotlin block comment because legacy/ is gradle-excluded. Acts as the
    canonical record of the 55 quarantined bindings so per-feature waves can
    copy-paste-restore each feature's slice without re-discovering signatures.
  - 3 build.gradle.kts updated with `kotlin.exclude("**/legacy/**")` in commonMain
    source-set: core/data, feature/checker-inbox-task, feature/settings.

Verification gates (all PASS):
  - 0 references to `com.mifos.core.network.datamanager.*` outside legacy/ folders
  - 61 .kt files inside */legacy/ folders project-wide
  - gradle exclusion present in all 3 module build files
  - RepositoryModule.kt compiles standalone (only live imports remain)

The legacy/ files are intentionally non-functional — they reference Room/Network
types whose new paths are post-B6, but the original DataManager dependency is gone.
They are READ-ONLY reference material for per-feature waves. Each Tier 1 wave deletes
its feature's slice of legacy/ in the same commit that lands the new vertical.

Next: Tier 1 / W1 — feature/about (smallest possible feature, pure leaf, validates
the per-wave playbook before tackling cross-linked features).
… into navigation + viewmodel files

Per user direction post-W1 audit: align feature/auth file structure with
kmp-project-template/feature/crypto's convention — routes inside the navigation
file, MVI sealed types at the bottom of the ViewModel file. Net: 8 files → 4
files (-50%) in feature/auth/src/commonMain/kotlin/com/mifos/feature/auth/.

Changes:
  - navigation/AuthNavigation.kt — now contains @serializable object LoginRoute
    (previously a separate LoginRoute.kt) alongside the existing navigateToLogin
    extension + authNavGraph builder. Single file holds all navigation concerns.
  - ui/LoginViewModel.kt — class body unchanged at top; LoginState, LoginEvent,
    LoginAction sealed types moved from their own files to the bottom of this
    file inside a `// region MVI types` block. Imports for StringResource added.
  - DELETED:
      navigation/LoginRoute.kt
      ui/LoginState.kt
      ui/LoginEvent.kt
      ui/LoginAction.kt

No behavioral change. Consumer LoginScreen.kt resolves the types from the same
package (LoginState / LoginEvent / LoginAction are now inside LoginViewModel.kt
but their package is unchanged). cmp-navigation imports LoginRoute via the new
AuthNavigation.kt — same import path, just the file containing the declaration
changed.

Aligns with kmp-project-template/feature/crypto/navigation/CryptoNavigation.kt
which carries CryptoGraphRoute + CryptoWatchlistRoute + CoinDetailRoute + the
navigateToCrypto + cryptoGraph all in one file.

This is the canonical pattern for W2+ to mirror. The MIGRATION_PLAYBOOK.md
reusable scaffold section has been updated (framework repo, separate commit).
@biplab1

biplab1 commented May 22, 2026

Copy link
Copy Markdown
Contributor

From PR description:

41 plain DTOs moved core/database → core/model/objects/

@therajanmaurya Shouldn't DTOs be placed inside :core:network, with :core:model containing only domain models?

@therajanmaurya

Copy link
Copy Markdown
Member Author

From PR description:

41 plain DTOs moved core/database → core/model/objects/

@therajanmaurya Shouldn't DTOs be placed inside :core:network, with :core:model containing only domain models?

These are plan data classes that I have moved, it's just the name

Rajan Maurya added 6 commits May 22, 2026 18:47
…places raw Scaffold + MifosProgressIndicatorOverlay)

Per user direction: the W1 alignment was incomplete — LoginScreen still wrapped
the form in a raw `Scaffold` and used the project-local `MifosProgressIndicatorOverlay`
for the submit-in-flight state. That bypasses the canonical mutation-screen pattern
established in core-base/ui (`template.core.base.ui.submit.MutationScreenContent`)
which auto-composes ScreenContent + SubmitProgressOverlay + SubmitResultHandler
for any mutation lifecycle.

The Scaffold here genuinely matters (the persistent "Update Server Configuration"
bottom-bar button + snackbar host) — that stays as the outer container. The form
body + submit lifecycle now flow through MutationScreenContent inside the Scaffold's
content area.

What changed in LoginScreen.kt:

  Old structure:
    Scaffold(bottomBar = serverConfigButton) { padding ->
        Column { LoginForm }
    }
    if (isSubmitting) { MifosProgressIndicatorOverlay() }   ← project-local overlay

  New structure:
    Scaffold(bottomBar = serverConfigButton) { padding ->
        MutationScreenContent<Unit, PostAuthenticationResponse>(
            screenState = ScreenState.Content(Unit, DataFreshness.FRESH),  ← degenerate; login has no fetched data
            submitState = submitState,
            onRetry = {},
            onSubmitted = { _ -> },  ← navigation handled by LoginViewModel via NavigateToPasscode Event
            modifier = Modifier.padding(padding),
        ) { _, _ ->
            LoginForm(state, onSubmit, isSubmitting = submitState is SubmitState.Submitting)
        }
    }

The previous monolithic `LoginContent` was split into `LoginContent` (Scaffold
shell + MutationScreenContent wiring) + `LoginForm` (pure form composable that
takes `state`, `onSubmit`, `isSubmitting`). This separates the framework-binding
concerns from the actual form layout — easier to preview, easier to reuse.

Previews updated to pass `submitState: SubmitState<PostAuthenticationResponse>`
instead of the previous `isSubmitting: Boolean` flag — the 3 @DevicePreview
variants are Idle / Submitting / ValidationError, each constructing the right
SubmitState directly.

Net behavior: same UX (form layout unchanged; submit progress overlay still
appears during the network call). Difference: the overlay is now the framework-
provided SubmitProgressOverlay (consistent across all mutation screens) instead
of the project-local MifosProgressIndicatorOverlay. Also unlocks: future
SubmitResultHandler integration for consistent error categorization across all
mutation screens.

The MIGRATION_PLAYBOOK.md (framework repo, separate batch commit) now carries
W1's full E2E architecture mapping table — first wave to author this canonical
artifact. Every future wave will document its 7-layer mapping (Compose →
ViewModel → Repository → Store builder → DAO → Api → Domain DTOs) before
authoring code.
…adle script compilation

CI failure on PR openMF#2691 commit 463426d (Test Coverage Floor workflow): script
compilation errors in cmp-android/build.gradle.kts — unresolved `libs.plugins.
android.application.convention`, `libs.plugins.android.application.compose.
convention`, `libs.plugins.aboutLibraries`. Root cause: build.gradle.kts files
across the project (cmp-android, core-base/*, libs/*) were sync'd from upstream
template via sync-dirs.yaml but the matching catalog entries in gradle/libs.
versions.toml weren't included in the sync (the toml is intentionally consumer-
local — it carries the project's package namespaces + version overrides).

This is the SAME failure mode that openMF/kmp-project-template#163 fixes
upstream by making sync-dirs self-healing. Until that PR merges + the next
sync-dirs run propagates the heal logic, field-officer needs a manual catch-up
of every catalog entry referenced by sync'd build.gradle.kts files.

Audit: project-wide grep of every `libs.<X>` reference in `**/*.kts` + `**/*.kt`
against gradle/libs.versions.toml found 16 unresolved aliases (180 total refs
scanned, 16 missing).

Added to [versions]:
  kmptoolkit = "3.2.3"
  aboutLibraries = "13.2.1"
  androidxHiltNavigationCompose = "1.2.0"
  androidxSecurityCrypto = "1.1.0-alpha06"
  bouncycastle = "1.78.1"
  sqliteWeb = "2.6.2"
  store = "5.1.0-alpha08"
  turbine = "1.2.1"

Added to [libraries]:
  aboutlibraries-core, aboutlibraries-compose-core, aboutlibraries-compose-m3
  androidx-hilt-navigation-compose
  androidx-security-crypto
  androidx-sqlite-web
  androidx-test-ext-junit (uses existing junitVersion = "1.2.1" which is
    actually the ext-junit version, not junit4)
  bouncycastle
  cmp-network-monitor + cmp-network-monitor-compose (both use kmptoolkit version)
  junit (uses existing junit4 = "4.13.2" — separate from junitVersion)
  kermit-koin
  store5 + store5-cache
  turbine

Added to [plugins]:
  aboutLibraries = { id = "com.mikepenz.aboutlibraries.plugin", … }
  android-application-convention = { id = "org.convention.android.application" }
  android-application-compose-convention = { id = "org.convention.android.application.compose" }

The 3 plugin convention aliases match plugin IDs registered in
build-logic/convention/build.gradle.kts — the registration was already there;
only the alias entry was missing.

Verification: full re-run of the libs-resolution audit (the exact algorithm
that the upstream PR openMF#163 ships as a sync-dirs heal step) now reports 0
unresolved references project-wide:
  scanned: 188 libs.* refs
  missing: 0

Categories of the 188 refs:
  - 14 in build-logic/convention/build.gradle.kts (fixed in commit 4077e85)
  - ~50 in cmp-android, cmp-shared, cmp-navigation, cmp-desktop, cmp-web, cmp-ios
  - ~80 in core-base/*, libs/* (vendored modules sync'd from template)
  - rest in feature/*, core/*

This unblocks the next CI run on PR openMF#2691. Expected outcome: kover coverage
job progresses past script compilation; surfaces whatever non-catalog compile
issues remain (likely a smaller surface from B4/B6/B6.10 cross-tier moves).
…s/mifos-passcode removal + sync-dirs heal

After commit ac2fbe2 (T0.1 quarantine) + da586a7 (16 alias catch-up), the
Test Coverage Floor workflow on PR openMF#2691 still failed at gradle script
compilation. Local reproduction with openjdk@17 surfaced a chain of compounding
issues; this commit fixes them so `./gradlew help` now succeeds.

`--no-verify` used per user direction — detekt has many pre-existing violations
across the codebase (long parameter lists, parameter order) unrelated to this
commit's changes. Detekt cleanup is a separate wave.

Major changes:

1. Gradle wrapper 8.13 → 9.5.0 (gradle/wrapper/*) — kmp-product-flavors:2.4.2
   ships Kotlin 2.2 metadata; Gradle 8.13's kotlin-dsl uses Kotlin 2.0.21 which
   max-reads metadata 2.1. Matches template's wrapper version.

2. Root build.gradle.kts — added:
   - buildscript classpath R8 9.1.31 pin (understands Kotlin 2.3.20 metadata)
   - Root kover + kover.convention plugin registration so KoverConventionPlugin
     can apply at per-module level
   - subprojects resolutionStrategy forcing JetBrains androidx.lifecycle 2.9.6
     + savedstate 1.3.6 (resolves KLIB resolver duplicate-warnings)
   - failOnNoDiscoveredTests = false per-test-task (Gradle 9 KMP compat)
   - roborazzi alias

3. gradle/libs.versions.toml — catalog catch-up:
   - Added missing version refs: appPackage
   - Added missing library alias: compottie (bare, alongside -lite/-resources;
     core-base/ui/build.gradle.kts uses `libs.compottie` directly which
     Gradle 9 rejected when only sub-aliases existed)
   - Stripped `version = "unspecified"` from 15 convention-plugin aliases
     (Gradle 9 rejects with "Error resolving plugin … plugin is already on
     the classpath with an unknown version")
   - Removed androidx-hilt-navigation-compose + androidxHiltNavigationCompose
     (field-officer is Koin-only per user direction; the only reference was
     in libs/mifos-passcode which is deleted in this commit)

4. Deleted libs/mifos-passcode/ entirely (109 files) — vendored legacy
   Hilt-based library excluded from build since 2024 (settings.gradle.kts
   had `//include(":libs:mifos-passcode")` commented). Functionality replaced
   by feature/passcode (Koin-based, scheduled W2 in
   active/feature-vertical-migration/PLAN.md). Removed commented include from
   settings.gradle.kts.

5. core/network/build.gradle.kts — removed `add("kspIosX64", libs.ktorfit.ksp)`
   (TS-2 dropped iosX64 target; this reference triggered "Configuration with
   name 'kspIosX64' not found" at config time).

6. sync-dirs self-heal (mirrors openMF/kmp-project-template PR openMF#163):
   - .github/workflows/sync-dirs.yaml: new "Heal libs.versions.toml" step
     between sync + temp-branch cleanup. Detects build-logic `libs.<X>` refs
     unresolved in consumer catalog, pulls them (and their referenced version
     keys) from upstream via `git show $TEMP_BRANCH:gradle/libs.versions.toml`.
   - sync-dirs.sh: matching heal_libs_versions_toml() function for local CLI.
   Eliminates the failure mode that required this commit's manual catch-up.

7. File renames for ktlint compliance:
   - core/ui/.../ViewModelTypes.kt → BaseViewModel.kt (single typealias)
   - feature/document/.../DocumentRoute.kt → DocumentListRoute.kt
   - feature/path-tracking/.../PathTrackingRoute.kt → PathTrackingScreenRoute.kt
   - feature/search/.../SearchRoute.kt → SearchScreenRoute.kt

8. core/data/.../legacy/di/RepositoryModule.legacy.kt — added @file:Suppress
   "ktlint:standard:no-empty-file" + a private object marker. The file is
   intentionally a /* */ block-commented reference (legacy/ is gradle-excluded)
   but spotless still scans it.

Verification (with openjdk@17):
  ✅ ./gradlew help              — BUILD SUCCESSFUL
  ✅ ./gradlew -p build-logic help — BUILD SUCCESSFUL
  ✅ ./gradlew spotlessApply     — BUILD SUCCESSFUL
  ⚠️  ./gradlew :cmp-android:assembleDemoDebug — 531/535 tasks succeed; real
     Kotlin compile errors remain in core/model (B6.10 leftover unresolved
     imports). Tracked for next commit.
  ⚠️  ./gradlew detekt           — Many pre-existing violations. Separate cleanup wave.

Run locally:
  export JAVA_HOME=/opt/homebrew/opt/openjdk@17
  export PATH=$JAVA_HOME/bin:$PATH
  ./gradlew help                # ← passes
  ./gradlew :cmp-android:assembleDemoDebug  # ← reaches kotlin compile

The build is unblocked at the gradle-script + catalog level. PR openMF#2691 will
now proceed further than commit da586a7 did in the Test Coverage Floor CI.
…direction + 10 file reverts + androidx.room→androidx.room3 bulk

Per user authorization to use --no-verify (detekt has pre-existing violations
unrelated to this commit; spotless deferred to post-migration cleanup wave).

Three categories of fixes for the Kotlin compile errors that remained after
commit 655ee2a (build-unblock):

### A. ApiDateFormatter dep-direction (5 files in core/model couldn't see core/common)

`com.mifos.core.common.utils.ApiDateFormatter` is referenced by 5 payload files
in core/model, but core/model has no dependency on core/common (core/common
already depends on core/model via `api(projects.core.model)`, so adding a
core/model → core/common dep would create a cycle).

Solution: moved both `ApiDateFormatter.kt` and `DateFormatPattern.kt` from
`core/common/utils` → `core/model/utils`. Consumers in core/common (e.g.,
FormatDate.kt) gained explicit imports; all 20 project-wide consumer paths
rewritten from `com.mifos.core.common.utils.{ApiDateFormatter,DateFormatPattern}`
→ `com.mifos.core.model.utils.{ApiDateFormatter,DateFormatPattern}`.

### B. 10 files reverted from core/model → core/database

Same pattern as B6.10's 18 reverts. These files were moved to core/model in
B6.10 but reference Room @entity types (LoanStatusEntity, ClientStatusEntity,
SavingAccountDepositTypeEntity, SavingsAccountWithAssociationsEntity,
DataTablePayload, etc.) that live in core/database. core/model can't see
core/database (would be circular).

Reverted (with package decl rewrites):
  - core/model/network/LoansPayload.kt → core/database/loan/entity/
  - core/model/objects/client/PageItem.kt → core/database/client/entity/
  - core/model/objects/loan/Loan.kt → core/database/loan/entity/
  - core/model/objects/loan/LoanApprovalData.kt → core/database/loan/entity/
  - core/model/objects/savings/SavingsSummaryData.kt → core/database/savings/entity/
  - core/model/objects/savings/SavingsTransactionData.kt → core/database/savings/entity/
  - core/model/objects/collectionsheet/CenterDetail.kt → core/database/collectionsheet/entity/
  - core/model/objects/collectionsheet/ClientCollectionSheet.kt → core/database/collectionsheet/entity/
  - core/model/objects/collectionsheet/GroupCollectionSheet.kt → core/database/collectionsheet/entity/
  - core/model/objects/collectionsheet/IndividualCollectionSheet.kt → core/database/collectionsheet/entity/

The last 2 (GroupCollectionSheet, IndividualCollectionSheet) revert as a
cascade — they reference ClientCollectionSheet which moved to core/database.

Also fixed: `RecurringDeposit.kt` got an explicit import for `Timeline` from
`core.model.objects.template.recurring.Timeline` (the field already declared
`val timeline: Timeline? = null` but no import; Kotlin compile-checked it after
the dep-direction fix surfaced this gap).

Consumer imports rewritten project-wide for all 10 moved types — every
`import com.mifos.core.model.objects.X.Y` switched to `import com.mifos.room.X.entity.Y`.

### C. androidx.room → androidx.room3 bulk rewrite

After TS-1 (commit 408d9cb) bumped Room 2.8.4 → 3.0.0-alpha05, the package
prefix changed from `androidx.room` → `androidx.room3`. Source files still
used the old prefix. Bulk-replaced project-wide:

  grep -rln '^import androidx\.room\.' --include='*.kt' | xargs sed -i
  's|^import androidx\.room\.|import androidx.room3.|'

Result: ~50+ files updated across core-base/database, core/database, and the
moved files in B above.

### Verification status

  ✅ ./gradlew :core:model:compileCommonMainKotlinMetadata — BUILD SUCCESSFUL
  ✅ ./gradlew :core-base:database:compileCommonMainKotlinMetadata — BUILD SUCCESSFUL
  ⚠️  ./gradlew :cmp-android:assembleDemoDebug fails on
     core-base/database/.../nonJsCommonMain/.../Room.nonJsCommon.kt:128 + :134:
     "'actual typealias Relation = Relation' has no corresponding members for
      expected class members: expect val parentColumn / entityColumn / constructor(...)".
     Room 3 alpha05 API mismatch with field-officer's expect declarations.
     Tracked as Item 2 (Room 3 alpha05 expect/actual signature reconciliation).
…ed + sync-dirs prune + PLAN taxonomy

Structural checkpoint on feat/store5-full-migration ahead of W2 (passcode) wave.

CORE-BASE:
- Byte-identical replace from kmp-project-template (~150 files)
- Removed 4 stale Room.kt typealias shim files (Room 3 alpha05 supports KMP natively)

CORE/DATABASE:
- DatabaseModule rewritten to template's destructive-fallback pattern across 5 platforms
- Manual MIGRATION_1_2..4_5 + local exec helper dropped (template uses AutoMigration declarative + destructive fallback only)
- 95 consumer files rewritten: template.core.base.database.* -> androidx.room3.*
- Stale schemas/com.mifos.room.db.MifosDatabase/ purged

CORE/DATA:
- RepositoryModule stripped to 13 active bindings (auth W1 + searchrecord W4 + note W7 + path-tracking W11 + activate + infra)
- 67 files quarantined to _legacy/ outside compile path (Impls + paging sources + mappers)
- 19 files rewritten: com.mifos.core.data.store typealias imports -> direct template.core.base.store.* (typealias does not expose nested types)
- 4 utility-file fixes: StoreReadResponse.Error<*> -> Error, NetworkMonitor import, submitHandler alias-import
- Added cmp-network-monitor + androidx.tracing.ktx + coreBase.store deps
- core/data + core/network + core/ui + core/database all build green individually

CMP-ANDROID:
- FileProvider consolidated, network_security_config added, Theme.AppSplash renamed (was Theme.AndroidClientTheme), provider_paths.xml replaces fileproviderpath.xml

SYNC-DIRS:
- sync-dirs.sh + .github/workflows/sync-dirs.yaml: added prune_stale_files() that diffs upstream vs local before git checkout, removes files no-longer-upstream (closes drift-class root cause that caused the Room.kt shim to linger)

NOT GREEN: feature/* modules still need realignment (~90 errors across screens from Compose Nav 2.8 typesafe + DesignToken imports + Store5 API churn). Per the PLAN's per-wave recipe -- W1 auth realignment + W2 passcode are next session's start.
- feature/auth: LoginRoute composable + stateIn import
- feature/passcode: BaseViewModel -> .viewmodel.BaseViewModel; composableWithSlideTransitions -> .nav
- feature/document: remove out-of-scope val unused = s lines
- feature/activate: add DesignToken import
- feature/settings: quarantine SyncSurveysDialogRepositoryImp (deferred to W20)
- feature/checker-inbox-task: quarantine CheckerInboxDialogViewmodel (deferred to W21)
- cmp-navigation: activate imports + popBackStack overload ambiguity fix
- cmp-android: org.mifos.* -> com.mifos.* namespace; UserDataRepository -> UserPreferencesRepository
- core/database (80 files): androidx.room3.* import-reorder sweep (spotless)
- core/data, core/model, core/domain, feature/*: residual spotless reformats

APK assembles, installs, launches on device; MainActivity in foreground;
no FATAL/crash in logcat. Checkpoint between W1 (auth) and W2 (passcode).

Committed with --no-verify; pre-existing detekt debt deferred to W23-tail
per feature-vertical-migration PLAN.
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants