feat: migrate from v1 to v2 db - #65
Open
OffRange wants to merge 29 commits into
Open
Conversation
…e name Gives callers outside :core:item a way to commit a batch of item writes as one atomic transaction without reaching for the module-internal ItemDatabase. Also locks in the v2 database file name so it can never collide with the inherited v1 secure_element_database file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…at versions 1-3 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rrect the schema guard docs The identity hash is computed over sorted fields and Room validates an opened file by column name, so column order is runtime-inert. Say that in the KDocs instead of the reverse, and state that the hash assertion and the DDL assertion cover different things so neither gets deleted as redundant. Record what actually happens when a version 2 file carries a NULL title or NULL data: the generated recreate aborts and the file cannot be opened at all. Also assert the cross ref cascade, count() on a populated database, and that favorite and modified_at survive the migrations. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A NULL title or data in a version 1 or 2 file aborts Room's generated 2-to-3 recreate and makes the whole database unopenable, costing the user every other row. Repair those two columns in place before Room ever sees the file, leaving the auto-migrations and the identity hash untouched. The repair is the smallest thing that clears the constraint: an empty title and an empty blob, never a deletion. An empty blob cannot decrypt, so the import reports that row as one failed item instead of losing the file. Guards against creating a missing file, touching an already-migrated version 3 file, or assuming SecureElement exists, and takes the driver as a parameter so tests run on the bundled driver. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tests Wraps the sanitizer's count read and both UPDATEs in a single transaction so a crash mid-repair cannot leave the file half-fixed, inlines the hasTable table name to remove an unescaped-literal pattern, and makes selectInt fail loudly on a query with no row instead of returning a garbage 0. Adds a version-1 repair test (the population this module exists for) and rewrites the version-3 guard test so it actually fails if the guard is removed, rather than vacuously passing because a real v3 file can never hold the NULL it was checking for. Turns the stalled agent's debug println into a real assertion pinning that an aborted 2-to-3 recreate rolls the file back to version 2, and fixes a KDoc typo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Wires the ported v1 database, the Keystore cipher and the detail parser into a repository that reads every row, decrypts it, parses it, and returns typed LegacyItems plus the rows it could not import. LegacyDatabaseProvider gains its implementation. It sanitizes the inherited file before Room can open it, because Room runs the 2-to-3 recreate on the first query and one NULL title or blob aborts it and takes every other row down with it. The repaired-row count is surfaced so the migration can report it, and a file that cannot even be sanitized becomes an answer rather than an exception thrown across the unlock flow. The Keystore alias is probed once per run instead of once per row. A gone alias means nothing in the file is recoverable, which is a different outcome from a single row that will not decrypt, and the two call for opposite handling. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The legacy reader only ever reads a database it inherited, but Room creates any file it is asked to open, so a read on an install that never ran v1 would leave an empty secure_element_database behind and every later run would find it and treat it as inherited data. This was a KDoc contract on LegacyItemRepository that callers had to remember. Make it true by construction instead: the provider returns null when the file is not there, next to the sanitizer call, since both guards are about the file's state before Room is allowed to touch it. state() keeps asking the file first so a clean install reads as Absent rather than Unreadable, which matters because those lead to opposite places. The provider gained a driver seam, left null in production so Room keeps the framework helper it has always used. Without it Room cannot create a file under a JVM test at all, and the test asserting no file appears would hold whether or not the guard existed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two stacked KDoc blocks orphaned the class documentation; only the @param block was attached to the declaration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
state() no longer folds every withDao query failure into NotLegacy. NotLegacy now rests only on LegacySecureElementProbe positively answering false that the SecureElement table is absent; a query failure (corrupt page, a disk that fills mid-recreate, a cancelled run) reports Unreadable instead, so a file that was never shown to be free of v1 data can no longer be deleted on a guess. The probe's hasSecureElementTable()/selectInt() extensions are lifted out of LegacyDatabaseSanitizer into a shared LegacySecureElementProbe so the sanitizer and the repository can never drift into disagreeing about what a v1 file is. Also: withDao rethrows CancellationException instead of folding it into DatabaseUnreadable through runCatching, so a run cancelled by the unlock scope going away no longer answers for what is on disk. SanitizingLegacyDatabaseProvider.repairedRows is marked @volatile since it is written under a lock but read through a plain getter from another thread. LegacyReadFailure is narrowed to internal. Deduplicates the schema-seeding test helper that was copy-pasted across LegacyDatabaseOpenTest, LegacyDatabaseSanitizerTest and SanitizingLegacyDatabaseProviderTest into one LegacySchemaSeed.kt, and adds LegacySecureElementProbeTest plus new LegacyItemRepositoryImplTest coverage for the state()/cancellation/deleteDatabase-ordering behaviour above. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…solation Gates the run on LegacyItemRepository.state(): Absent does nothing, NotLegacy deletes the stale file, Unreadable is left exactly where it was found, and only Present reads. Rows that fail are reported and left behind; the ones that made it are pruned, so a retry cannot duplicate anything. Deleting the inherited file and v1's Keystore alias only happens once the data is provably in v2: no failed row, a successful prune, and a legacy file that counts zero rows afterwards. The alias goes last and only if the file went. Corrects four defects in the plan's version of this step: - readAll/prune/remainingCount return Result, not raw values. Transcribing the planned call would have read a failed read as an empty file and deleted the user's data unseen. - LegacyDatabaseState.Unreadable had no branch at all. - The Keystore alias was deleted whether or not the file actually went, which would leave encrypted rows on disk with nothing left to open them. - A cryptographic scope that will not open was blamed on the row as UndecryptablePassword, once per row. It now stops the run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Runs MigrateLegacyDataUseCase on all four ways into the app: password create access, biometric create access, password unlock, and biometric unlock. The biometric unlock path finished in the composable and navigated on its own, so it now hands back to the ViewModel through onBiometricUnlockSucceeded and navigates via navigationEvent instead; left as it was it would have been the one entry point that never retried a partial import, and the one most returning users take daily. The import is waited for rather than launched and forgotten, because MainActivity pops AuthRoute with popUpTo(inclusive), which clears this ViewModel and cancels the scope underneath it. An import left running would be killed on every unlock and never finish. That wait is what importLegacyData exists to make safe. Unlock is how the user gets back to their data, so nothing about an optional one-off copy may stand between them and it. Corrects three defects in the plan's version of this step: - migrateLegacyData() was called bare inside viewModelScope.launch. Its invoke() is not total: state() and the NotLegacy deleteDatabase() sit outside its own catch, and it rethrows CancellationException on purpose. A throw skipped handleAuthenticationResult, so the navigation event never fired and the loading flag never cleared, stranding a user whose session was already live on the lock screen behind a permanent spinner. It is now caught, and a run that stops making progress is dropped after 30 seconds. - the new biometric path ended in trySend on a rendezvous channel whose only collector runs under repeatOnLifecycle(STARTED). With a migration now sitting between the unlock and the send, backgrounding the app during it dropped the event and left the user locked out of an already unlocked vault. Uses send. - feature/auth alone could not resolve the Koin definition, since implementation does not put the module on :app's classpath. Adds the dependency to app/build.gradle.kts, mirroring migration:create-access. The plan's fallback pointed at app/di/Koin.kt, which does not exist in this repo. AuthViewModel cannot be built in a JVM unit test (SavedStateHandle.toRoute lands in android.os.BaseBundle), so the behaviour lives in importLegacyData, which takes the migration as a lambda and is tested directly with no mocks. AuthViewModelMigrationTest guards the call sites, including that none of them calls the use case unguarded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The import was awaited inside the unlock flow under a 30s budget. That put the whole thing on the main thread (nothing in :migration:legacy-data hops off Main, and the per-row decrypt loop has no suspension point for the timeout to land on), inside the compare-and-set lambda of MutableStateFlow.update, and ahead of the navigation event. The budget could not help: the import is atomic, so a database too big to finish in 30s rolls back, prunes nothing and dies at the same point on every unlock, forever. Start it on an application-scoped coroutine on Dispatchers.IO instead and return at once. Unlock latency stops depending on the size of the user's v1 database, and with no wait there is nothing to budget. StartLegacyDataImportUseCase lives in :migration:legacy-data next to the use case it drives, so migration-only code stays in the migration module and @componentscan picks it up unchanged. Its LegacyImportRunner holds the single-flight gate: two concurrent runs would import every row twice under ids neither run can recognise as the other's, and a second unlock during a run is reachable on its own. Nothing escapes into the app scope, where an uncaught throwable would take the process down, and the throwable is now logged rather than discarded. Locking the vault mid-run fails that run, which leaves the legacy file as it found it and retries on the next unlock. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LegacyImportRunner discarded the returned LegacyMigrationOutcome, so every expected failure from MigrateLegacyDataUseCase (Failed, and a Migrated with row failures) produced no signal at all. The seam now reports Failed's cause and a summary of row failures by reason, while leaving the clean endings silent. Also corrects the KDoc's false claim about a lock interrupting an in-flight run (no such lock exists today) and closes the gap where a throwing report implementation could still reach the scope.
Wires the real Room database, v1's real AES-GCM cipher, the real detail parser and the real converter together and drives them through the real use case. Two properties only exist between links and so could not be proved by the per-component tests: v1's double encryption, where a password comes back as plaintext only if the repository, the parser and the converter each undo their own layer, and the prune-then-delete order, where the inherited file is destroyed only once the rows it held are provably in v2. The probe is the production one over a real file rather than a fake, so the NotLegacy verdict that deletes the user's file is earned from the bytes each test put on disk. The stale v2 fixture is therefore a real database with no SecureElement table; a file of arbitrary bytes is a corrupt file, which the probe answers null for and which must be left where it was found. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…2e suite The end-to-end no-data-loss suite drove the probe's true and false answers but never null, so a mutation folding Unreadable into NotLegacy (== false -> != true) would delete a corrupt or half-restored file and take the v1 Keystore alias with it while every existing test stayed green. Add a test that corrupts the database file directly, confirming the run fails and the file and Keystore alias survive. Also read back the migrated credit card's fields, which were counted but never asserted, and note why readBackPassword's missing wrapped item key is only safe because the fake crypto provider ignores it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ould not clear SQLite's bound-parameter limit was 999 until 3.32.0, a ceiling Android's system SQLite still enforces through API 30. prune() bound one parameter per legacy id in a single IN (...) delete with no chunking, so a v1 vault over that size would have every prune throw, be reported as a clean success, and reimport the whole vault on every subsequent unlock. Chunk the delete, and give the report a fileRetained flag so a prune/count/delete failure is reported even when no row failed to read or convert - hasFailures alone couldn't see that ending. Found by the final whole-branch review across all 22 commits on this branch.
There was a problem hiding this comment.
Pull request overview
This PR introduces the infrastructure to migrate legacy KeyGo v1 data into the v2 vault, adds supporting migration modules, and updates core domain/data layers (timestamps, transactions, session start signaling) to support reliable import execution without conflicting with the legacy on-disk database.
Changes:
- Added a new
:migration:legacy-datamodule that can open and migrate the v1secure_element_database, read/decrypt/parse rows, convert them into v2 items, and import them atomically with extensive JVM test coverage. - Added a new
:migration:create-accessmodule to read/validate/clear the v1 “main password” state via Proto DataStore. - Introduced item timestamps + an
ItemTransactionRunnerabstraction, addedSession.sessionStartsto trigger import on every unlock, and renamed the v2 database file to avoid colliding with the legacy v1 database.
Reviewed changes
Copilot reviewed 112 out of 133 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| settings.gradle.kts | Registers new nested migration modules (:migration:create-access, :migration:legacy-data). |
| scripts/new-module.sh | Improves key-read reliability and PascalCase generation for module scaffolding. |
| migration/legacy-data/src/testFixtures/kotlin/de/davis/keygo/migration/legacy_data/data/FakeRegistrableDomainResolver.kt | Adds deterministic domain resolver fake for migration tests. |
| migration/legacy-data/src/testFixtures/kotlin/de/davis/keygo/migration/legacy_data/data/FakeLegacyKeyRepository.kt | Adds fake Keystore-backed legacy key repository for tests. |
| migration/legacy-data/src/testFixtures/kotlin/de/davis/keygo/migration/legacy_data/data/FakeLegacyItemRepository.kt | Adds in-memory legacy item repository fake for tests. |
| migration/legacy-data/src/testFixtures/kotlin/de/davis/keygo/migration/legacy_data/data/FakeLegacyElementDao.kt | Adds in-memory DAO fake for pruning/counting tests. |
| migration/legacy-data/src/testFixtures/kotlin/de/davis/keygo/migration/legacy_data/data/FakeLegacyDatabaseProvider.kt | Adds fake legacy DB provider for tests (open/close/delete behavior). |
| migration/legacy-data/src/testFixtures/kotlin/de/davis/keygo/migration/legacy_data/data/FakeLegacyDatabase.kt | Adds lightweight fake Room database container for tests. |
| migration/legacy-data/src/testFixtures/kotlin/de/davis/keygo/migration/legacy_data/data/FakeLegacyCipher.kt | Adds reversible cipher fake + failure modes for migration tests. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/LegacyMigrationRealDatabaseTest.kt | Adds “real captured v1 DB” end-to-end migration test. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/domain/usecase/LegacyImportRunnerTest.kt | Adds concurrency/containment/retry semantics tests for the import runner. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/domain/mapper/LegacyItemConverterTest.kt | Adds converter tests for passwords/cards, strength mapping, timestamps, domain parsing. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/data/repository/LegacyItemRepositoryImplTest.kt | Adds repository tests for read/prune/count/delete + file-creation guard. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/data/local/LegacyTestContext.kt | Adds minimal Context stub to open legacy DB via production provider in JVM tests. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/data/local/LegacySchemaSeed.kt | Adds helpers to seed DBs from v1 exported schemas for migration tests. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/data/local/LegacySchemaIdentityTest.kt | Pins Room identity hashes to ensure ported legacy schema stays byte-faithful. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/data/local/LegacyMigrationTest.kt | Tests the handwritten v1 2→3 migration behavior and schema correctness. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/data/local/LegacyDatabaseOpenTest.kt | Verifies ported entities + migrations open v1 DB files across versions. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/data/json/LegacyDetailParserTest.kt | Adds parser tests for v1 JSON quirks and malformed input tolerance. |
| migration/legacy-data/src/test/kotlin/de/davis/keygo/migration/legacy_data/data/crypto/LegacyAesGcmCipherTest.kt | Adds crypto framing tests for v1 AES-GCM format. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/usecase/MigrateLegacyDataUseCase.kt | Implements the v1→v2 import orchestration + safe deletion gating. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/usecase/LegacyImportRunner.kt | Adds single-flight, retry-on-unlock runner with containment boundary. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/usecase/LegacyDataImportStarter.kt | Wires import execution to every session start in an application IO scope. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/repository/LegacyKeyRepository.kt | Introduces legacy Keystore key access abstraction. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/repository/LegacyItemRepository.kt | Defines legacy DB read/prune/count/delete contract and read result model. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/model/LegacyStrength.kt | Adds v1 password strength enum in ordinal order (load-bearing). |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/model/LegacyReadFailure.kt | Adds whole-run read failure reasons (empty/unreadable/key unavailable). |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/model/LegacyMigrationReport.kt | Adds migration report + row failure detail model. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/model/LegacyMigrationOutcome.kt | Adds outcome sealed interface + migration exception type. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/model/LegacyItem.kt | Adds legacy row domain model + legacy tag prefix constant. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/model/LegacyFailureReason.kt | Adds per-row failure reasons for migration reporting. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/model/LegacyDetail.kt | Adds decoded v1 detail models + type discriminator constants. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/mapper/LegacyItemConverter.kt | Converts legacy rows into v2 items and re-encrypts secrets under v2 scope. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/domain/crypto/LegacyCipher.kt | Adds legacy cipher interface to decrypt v1 blobs using resolved key. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/di/MigrationLegacyDataModule.kt | Adds Koin module scanning + includes DB module wiring. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/di/LegacyDatabaseModule.kt | Provides LegacyDatabaseProvider via Koin. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/repository/LegacyKeyRepositoryImpl.kt | Implements legacy Keystore alias lookup + deletion (non-throwing). |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/repository/LegacyItemRepositoryImpl.kt | Implements legacy Room-based read/prune/count/delete with safety gating. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/mapper/LegacyElementMapper.kt | Maps decrypted rows to LegacyItem while filtering discriminator tags. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/local/pojo/LegacyElementWithTags.kt | Adds Room relation POJO for SecureElement + tags junction. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/local/migration/LegacyMigration2To3.kt | Adds handwritten v1 2→3 migration handling null title/data safely. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/local/entity/LegacyTagEntity.kt | Ports v1 Tag entity. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/local/entity/LegacySecureElementTagCrossRef.kt | Ports v1 junction entity with FK cascades. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/local/entity/LegacySecureElementEntity.kt | Ports v1 SecureElement entity with schema fidelity constraints. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/local/datasource/LegacyDatabaseProvider.kt | Defines provider contract that must not create legacy DB files. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/local/datasource/LegacyDatabase.kt | Defines Room3 legacy database + auto-migration 1→2. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/local/datasource/AndroidLegacyDatabaseProvider.kt | Implements “open legacy file only if exists” guard + registers 2→3 migration. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/local/dao/LegacyElementDao.kt | Adds DAO queries for read/count/prune and insert-only-for-tests helpers. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/json/LegacyDetailParser.kt | Parses v1 GSON-shaped JSON into strongly typed legacy details. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/json/LegacyDetailJson.kt | Serializable JSON mirror of v1 detail payload shapes. |
| migration/legacy-data/src/main/kotlin/de/davis/keygo/migration/legacy_data/data/crypto/LegacyAesGcmCipher.kt | Implements v1 AES-GCM blob decryption (IV prefix framing). |
| migration/legacy-data/schemas/de.davis.keygo.migration.legacy_data.data.local.datasource.LegacyDatabase/3.json | Adds exported Room schema for legacy DB v3. |
| migration/legacy-data/schemas/de.davis.keygo.migration.legacy_data.data.local.datasource.LegacyDatabase/2.json | Adds exported Room schema for legacy DB v2. |
| migration/legacy-data/schemas/de.davis.keygo.migration.legacy_data.data.local.datasource.LegacyDatabase/1.json | Adds exported Room schema for legacy DB v1. |
| migration/legacy-data/consumer-rules.pro | Consumer ProGuard/R8 rules for legacy-data module. |
| migration/legacy-data/build.gradle.kts | Adds module build config, dependencies, Room3 schema export/test seeding wiring. |
| migration/create-access/src/main/proto/main_password.proto | Defines Proto DataStore model for legacy main password data. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/domain/usecase/ValidateMainPasswordUseCase.kt | Renames and tidies main password validation use case. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/domain/usecase/HasMainPasswordUseCase.kt | Minor formatting/update. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/domain/usecase/ClearMainPasswordUseCase.kt | Minor formatting/update. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/domain/repository/MainPasswordRepository.kt | Minor formatting/update. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/domain/repository/HashValidator.kt | Minor formatting/update. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/domain/model/MainPassword.kt | Minor formatting/update. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/di/MigrationCreateAccessModule.kt | Provides qualified Proto DataStore and enables component scan. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/di/annotation/MainPasswordQualifier.kt | Adds Koin qualifier annotation for main password DataStore. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/data/repository/MainPasswordRepositoryImpl.kt | Implements DataStore-backed main password repository. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/data/repository/HashValidatorImpl.kt | Minor formatting/update. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/data/mapper/MainPasswordMapper.kt | Maps proto timestamp + hash into domain model. |
| migration/create-access/src/main/kotlin/de/davis/keygo/migration/create_access/data/local/datasource/datastore/MainPasswordSerializer.kt | Serializer for Proto DataStore read/write. |
| migration/create-access/src/main/AndroidManifest.xml | Adds module manifest stub. |
| migration/create-access/proguard-rules.pro | Adds module proguard rules template. |
| migration/create-access/consumer-rules.pro | Adds consumer rules for protobuf/datastore keep rules. |
| migration/create-access/build.gradle.kts | Adds module build config + DataStore/protobuf/bcrypt deps. |
| migration/create-access/.gitignore | Ignores module build output. |
| migration-create-access/src/main/AndroidManifest.xml | Removes old module manifest (module path renamed/restructured). |
| gradle/libs.versions.toml | Adds Room3 version + coordinates/plugin aliases. |
| feature/vault/src/test/kotlin/de/davis/keygo/feature/vault/domain/usecase/MoveItemsToVaultUseCaseTest.kt | Updates Login construction to include timestamp. |
| feature/item/view/src/test/kotlin/de/davis/keygo/feature/item/view/creditcard/ViewCreditCardViewModelTest.kt | Updates CreditCard construction to include timestamp. |
| feature/item/core/src/test/kotlin/de/davis/keygo/feature/item/core/domain/usecase/CreateNewOrUpdateLoginUseCaseTest.kt | Adds timestamp behavior tests + updates helpers for timestamp. |
| feature/item/core/src/test/kotlin/de/davis/keygo/feature/item/core/domain/usecase/CreateNewOrUpdateCreditCardUseCaseTest.kt | Adds timestamp behavior tests + updates helpers for timestamp. |
| feature/item/core/src/main/kotlin/de/davis/keygo/feature/item/core/domain/usecase/CreateOrUpdateItemUseCase.kt | Adds touch() seam and sets modifiedAt on update in shared flow. |
| feature/item/core/src/main/kotlin/de/davis/keygo/feature/item/core/domain/usecase/CreateNewOrUpdateLoginUseCase.kt | Implements touch() and sets timestamp on create. |
| feature/item/core/src/main/kotlin/de/davis/keygo/feature/item/core/domain/usecase/CreateNewOrUpdateCreditCardUseCase.kt | Implements touch(), uses shared expiration parser, sets timestamp on create. |
| feature/autofill/src/test/kotlin/de/davis/keygo/feature/autofill/presentation/dataset/SuggestionFinderTest.kt | Updates Login test fixtures to include timestamp. |
| feature/autofill/src/test/kotlin/de/davis/keygo/feature/autofill/presentation/activity/AutofillViewModelTest.kt | Updates Login test fixture to include timestamp. |
| feature/autofill/src/test/kotlin/de/davis/keygo/feature/autofill/domain/usecase/DoesItemHaveDomainReferencesUseCaseTest.kt | Updates Login test fixture to include timestamp. |
| feature/autofill/src/test/kotlin/de/davis/keygo/feature/autofill/domain/usecase/AddRegistrableDomainsToLoginUseCaseTest.kt | Updates Login test fixture to include timestamp. |
| feature/auth/src/main/kotlin/de/davis/keygo/feature/auth/presentation/AuthViewModel.kt | Uses renamed main-password validator and adds biometric-unlock success hook. |
| feature/auth/src/main/kotlin/de/davis/keygo/feature/auth/presentation/AuthScreen.kt | Routes biometric success through ViewModel to avoid skipping legacy import flow. |
| feature/auth/build.gradle.kts | Updates dependency path for renamed migration module. |
| core/security/src/testFixtures/kotlin/de/davis/keygo/core/security/crypto/FakeSession.kt | Adds sessionStarts emission flow to session fake. |
| core/security/src/testFixtures/kotlin/de/davis/keygo/core/security/crypto/FakeCryptographicScopeProvider.kt | Adds injectable failure mode for itemScope to support migration tests. |
| core/security/src/test/kotlin/de/davis/keygo/core/security/domain/usecase/ItemWithCryptoScopeUseCaseTest.kt | Updates CreditCard fixture to include timestamp. |
| core/security/src/test/kotlin/de/davis/keygo/core/security/data/SessionImplTest.kt | Adds tests ensuring sessionStarts is replayed and not dropped. |
| core/security/src/main/kotlin/de/davis/keygo/core/security/domain/Session.kt | Adds sessionStarts: Flow<Unit> to signal unlocks across entry points. |
| core/security/src/main/kotlin/de/davis/keygo/core/security/data/SessionImpl.kt | Implements replayed, non-suspending sessionStarts emission. |
| core/item/src/testFixtures/kotlin/de/davis/keygo/core/item/FakeItemTransactionRunner.kt | Adds fake transaction runner for atomic import/write tests. |
| core/item/src/testFixtures/kotlin/de/davis/keygo/core/item/FakeCreditCardRepository.kt | Exposes store state for migration and tests. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/domain/usecase/UpsertItemUseCaseTest.kt | Updates item fixtures to include timestamp. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/domain/usecase/ObserveAllTagsSortedUseCaseTest.kt | Updates Login fixture to include timestamp. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/domain/model/LoginTest.kt | Updates Login constructor usage for timestamp. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/domain/model/CreditCardTest.kt | Updates CreditCard constructor usage for timestamp. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/data/repository/LoginRepositoryImplTest.kt | Updates Login fixture to include timestamp. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/data/repository/ItemTransactionRunnerImplTest.kt | Adds tests for transaction runner delegation semantics. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/data/repository/CreditCardRepositoryImplTest.kt | Updates fixtures and projections to include timestamp mapping. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/data/mapper/LoginMapperTest.kt | Updates mapping expectations for timestamp fields. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/data/mapper/ItemMapperTest.kt | Updates ItemEntity mapping to include timestamp. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/data/mapper/DomainMapperTest.kt | Updates Login fixture to include timestamp. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/data/mapper/CreditCardMapperTest.kt | Updates mapping expectations for timestamp fields. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/data/local/dao/TagDaoTest.kt | Seeds ItemEntity with timestamp for DB tests. |
| core/item/src/test/kotlin/de/davis/keygo/core/item/data/local/dao/ItemDaoSearchTest.kt | Seeds ItemEntity with timestamp for DB tests. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/domain/repository/ItemTransactionRunner.kt | Introduces transaction abstraction to commit batch writes atomically. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/domain/model/Timestamp.kt | Adds domain timestamp model (createdAt/modifiedAt). |
| core/item/src/main/kotlin/de/davis/keygo/core/item/domain/model/Login.kt | Adds timestamp to Login item domain model. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/domain/model/Item.kt | Adds timestamp to Item interface. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/domain/model/CreditCard.kt | Adds timestamp to CreditCard item domain model. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/data/repository/ItemTransactionRunnerImpl.kt | Implements ItemTransactionRunner via Room withTransaction. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/data/mapper/TimestampMapper.kt | Maps between domain and entity timestamp representations. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/data/mapper/LoginMapper.kt | Maps timestamp from ItemEntity into domain Login. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/data/mapper/ItemMapper.kt | Persists domain timestamp into ItemEntity. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/data/mapper/CreditCardMapper.kt | Maps timestamp from ItemEntity into domain CreditCard. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/data/local/entity/Timestamp.kt | Adds embedded entity timestamp for the item table. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/data/local/entity/ItemEntity.kt | Embeds timestamp into ItemEntity persistence model. |
| core/item/src/main/kotlin/de/davis/keygo/core/item/data/local/datasource/ItemDatabase.kt | Renames v2 DB file to keygo_database to avoid legacy file collision. |
| core/item/schemas/de.davis.keygo.core.item.data.local.datasource.ItemDatabase/1.json | Updates schema snapshot to include timestamp columns and new identity hash. |
| build.gradle.kts | Applies Room3 plugin alias at root for module use. |
| app/build.gradle.kts | Wires new migration modules into the app module. |
| .gitignore | Ignores Room plugin schema-copy lock files (*.lck). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The refactor commit changed LegacyAesGcmCipherTest and LegacyMigrationEndToEndTest to import encryptLikeV1 from de.davis.keygo.migration.legacy_data.data as a shared top-level function, but never actually created it there — it only ever existed as a private helper inside the test class it replaced. CI has been failing on the unresolved reference since. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.