Output:
Chunk ID: 26aed6
Wall time: 0.2037 seconds
Process exited with code 0
Original token count: 5668
Output:
tools/billing/start-play-emulator.sh
tools/billing/adb_web_control.py
GEMINI.md
CLAUDE.md
AGENTS.md
TESTING_SETUP.md
distribution/store-listing.md
settings.gradle.kts
gradlew.bat
gradlew
gradle.properties
build.gradle.kts
sample-vault/generate.py
sample-vault/README.md
distribution/whatsnew/whatsnew-en-US
distribution/whatsnew/README.md
gradle/libs.versions.toml
distribution/store-assets/screenshots/tablet-7/screen_06_paywall_dialog.png
distribution/store-assets/screenshots/tablet-7/screen_05_settings_privacy.png
distribution/store-assets/screenshots/tablet-7/screen_04_categories.png
distribution/store-assets/screenshots/tablet-7/screen_03_add_record.png
distribution/store-assets/screenshots/tablet-7/screen_02_vehicle_detail.png
distribution/store-assets/screenshots/tablet-7/screen_01_vehicle_list.png
gradle/wrapper/gradle-wrapper.properties
gradle/wrapper/gradle-wrapper.jar
distribution/store-assets/screenshots/tablet-10/screen_06_paywall_dialog.png
distribution/store-assets/screenshots/tablet-10/screen_05_settings_privacy.png
distribution/store-assets/screenshots/tablet-10/screen_04_categories.png
distribution/store-assets/screenshots/tablet-10/screen_03_add_record.png
distribution/store-assets/screenshots/tablet-10/screen_02_vehicle_detail.png
distribution/store-assets/screenshots/tablet-10/screen_01_vehicle_list.png
distribution/store-assets/screenshots/phone/screen_06_paywall_dialog.png
distribution/store-assets/screenshots/phone/screen_05_settings_privacy.png
distribution/store-assets/screenshots/phone/screen_04_categories.png
distribution/store-assets/screenshots/phone/screen_03_add_record.png
distribution/store-assets/screenshots/phone/screen_02_vehicle_detail.png
distribution/store-assets/screenshots/phone/screen_01_vehicle_list.png
app/build.gradle.kts
app/proguard-rules.pro
app/lint-baseline.xml
app/detekt-baseline.xml
docs/billing-sandbox.md
config/detekt/detekt.yml
docs/plan-regression-tests.md
docs/dependabot-workflow.md
docs/specs.md
docs/sentry-setup.md
docs/privacy-policy.md
docs/plan-smoke-tests.md
docs/plan-paywall.md
docs/plan-integration-tests-restructure.md
docs/adr.md
docs/adr-011-navigation-transitions.md
app/coverage-baselines/gmd_smoke.ec
app/keystore.properties.template
app/src/androidTest/assets/test_receipt.pdf
app/src/main/java/com/yorvana/MainActivity.kt
app/src/main/java/com/yorvana/YorvanaApplication.kt
app/src/main/java/com/yorvana/util/VaultPathHelper.kt
app/src/main/java/com/yorvana/util/DateFormatter.kt
app/src/main/java/com/yorvana/util/UuidGenerator.kt
app/src/main/java/com/yorvana/util/SentryWrapper.kt
app/src/main/java/com/yorvana/util/SentryInitParams.kt
app/src/main/java/com/yorvana/util/FileProviderHelper.kt
app/src/main/java/com/yorvana/util/CrashReporter.kt
app/src/androidTest/assets/screenshots-fixture/vehicles/2019-honda-civic/vehicle.json
app/src/androidTestStaging/java/com/yorvana/data/storage/VaultStorageTestHooks.kt
app/src/main/java/com/yorvana/ui/TestTags.kt
app/src/androidTest/assets/screenshots-fixture/vehicles/2019-honda-civic/records/2025-07-15-coolant-flush.json
app/src/androidTest/assets/screenshots-fixture/vehicles/2019-honda-civic/records/2025-04-02-battery.json
app/src/androidTest/assets/screenshots-fixture/vehicles/2019-honda-civic/records/2025-01-18-mot.json
app/src/androidTest/assets/screenshots-fixture/vehicles/2019-honda-civic/records/2024-09-21-tyre-rotation.json
app/src/androidTest/assets/screenshots-fixture/vehicles/2019-honda-civic/records/2024-06-04-brake-pads.json
app/src/androidTest/assets/screenshots-fixture/vehicles/2019-honda-civic/records/2024-03-12-oil-change.json
app/src/androidTest/assets/screenshots-fixture/categories.json
app/src/androidTest/assets/test_receipt.jpg
app/src/test/java/com/yorvana/testsupport/RegressionTierScannerTest.kt
app/src/main/java/com/yorvana/ui/vehicles/VehicleListScreen.kt
app/src/main/java/com/yorvana/ui/vehicles/AddEditVehicleScreen.kt
app/src/main/java/com/yorvana/ui/vehicles/VehicleListViewModel.kt
app/src/main/java/com/yorvana/ui/vehicles/AddEditVehicleViewModel.kt
app/src/main/java/com/yorvana/ui/vehicles/VehiclePreview.kt
app/src/test/java/com/yorvana/util/FileProviderHelperTest.kt
app/src/test/java/com/yorvana/util/DateFormatterTest.kt
app/src/test/java/com/yorvana/util/CrashReporterTest.kt
app/src/test/java/com/yorvana/util/VaultPathHelperTest.kt
app/src/test/java/com/yorvana/util/UuidGeneratorTest.kt
app/src/test/java/com/yorvana/util/SentryWrapperTest.kt
app/src/main/java/com/yorvana/ui/util/BillingUIUtils.kt
app/src/main/java/com/yorvana/ui/util/ViewModelFactory.kt
app/src/main/java/com/yorvana/ui/util/EmailLaunchRequest.kt
app/src/main/java/com/yorvana/ui/util/ViewModelDependencies.kt
app/src/main/java/com/yorvana/ui/util/GatedViewModel.kt
app/src/main/java/com/yorvana/ui/util/ContextExtensions.kt
app/src/main/java/com/yorvana/ui/theme/Type.kt
app/src/main/java/com/yorvana/ui/theme/Theme.kt
app/src/main/java/com/yorvana/ui/theme/Color.kt
app/src/debug/java/com/yorvana/data/storage/VaultStorageTestHooks.kt
app/src/main/java/com/yorvana/ui/settings/SettingsViewModel.kt
app/src/main/java/com/yorvana/ui/settings/SettingsScreen.kt
app/src/main/java/com/yorvana/ui/settings/CategoriesViewModel.kt
app/src/main/java/com/yorvana/ui/settings/CategoriesScreen.kt
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenTest.kt
app/src/androidTest/java/com/yorvana/regression/billing/BillingSandboxRegressionTest.kt
app/src/main/java/com/yorvana/ui/navigation/AppNavGraph.kt
app/src/main/java/com/yorvana/ui/reportbug/ReportBugViewModel.kt
app/src/main/java/com/yorvana/ui/navigation/Screen.kt
app/src/main/java/com/yorvana/ui/reportbug/ReportBugScreen.kt
app/src/main/java/com/yorvana/ui/reportbug/DeviceInfoDialog.kt
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt
app/src/main/java/com/yorvana/ui/records/ImageViewerScreen.kt
app/src/main/java/com/yorvana/ui/records/VehicleDetailViewModel.kt
app/src/main/java/com/yorvana/ui/records/VehicleDetailScreen.kt
app/src/main/java/com/yorvana/ui/records/RecordDetailViewModel.kt
app/src/main/java/com/yorvana/ui/records/PerformerUtil.kt
app/src/main/java/com/yorvana/ui/records/AddEditRecordViewModel.kt
app/src/main/java/com/yorvana/ui/records/AddEditRecordScreen.kt
app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt
app/src/androidTest/java/com/yorvana/regression/RegressionScaffoldTest.kt
app/src/androidTest/java/com/yorvana/SmokeTest.kt
app/src/main/java/com/yorvana/ui/components/PaywallInfoSheet.kt
app/src/main/java/com/yorvana/ui/components/CurrencyPickerDialog.kt
app/src/main/java/com/yorvana/ui/components/MakePickerDialog.kt
app/src/main/java/com/yorvana/ui/components/UpgradeDialog.kt
app/src/test/java/com/yorvana/ui/vehicles/snapshots/AddEditVehicleScreen_add_dark.png
app/src/androidTest/java/com/yorvana/regression/attachments/ImageViewerGestureRegressionTest.kt
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt
app/src/main/java/com/yorvana/ui/components/TopBarBackButton.kt
app/src/main/java/com/yorvana/ui/components/ShimmerBrush.kt
app/src/main/java/com/yorvana/ui/components/RequiredLabel.kt
app/src/main/java/com/yorvana/ui/components/PrivacyPolicyDialog.kt
app/src/main/java/com/yorvana/ui/components/PrivacyPolicyContent.kt
app/src/main/java/com/yorvana/ui/components/PaywallStatusCard.kt
app/src/main/java/com/yorvana/ui/components/PaywallScaffold.kt
app/src/test/java/com/yorvana/ui/vehicles/snapshots/AddEditVehicleScreen_add_light.png
app/src/main/java/com/yorvana/ui/components/ExpandablePrivacyDisclosure.kt
app/src/test/java/com/yorvana/ui/vehicles/snapshots/AddEditVehicleScreen_edit_dark.png
app/src/main/java/com/yorvana/ui/components/ErrorListItem.kt
app/src/test/java/com/yorvana/ui/vehicles/snapshots/VehicleListScreen_list_dark.png
app/src/main/java/com/yorvana/ui/components/EmptyStateView.kt
app/src/test/java/com/yorvana/ui/vehicles/snapshots/AddEditVehicleScreen_edit_light.png
app/src/test/java/com/yorvana/ui/vehicles/snapshots/VehicleListScreen_list_light.png
app/src/main/java/com/yorvana/ui/components/ConfirmDeleteDialog.kt
app/src/test/java/com/yorvana/ui/vehicles/snapshots/VehicleListScreen_readOnly_light.png
app/src/main/java/com/yorvana/ui/components/ChromeLikeMenu.kt
app/src/test/java/com/yorvana/ui/vehicles/snapshots/VehicleListScreen_empty_dark.png
app/src/main/java/com/yorvana/ui/components/AttachmentThumbnail.kt
app/src/test/java/com/yorvana/ui/vehicles/snapshots/VehicleListScreen_readOnly_dark.png
app/src/main/java/com/yorvana/ui/components/AttachmentChip.kt
app/src/test/java/com/yorvana/ui/vehicles/snapshots/VehicleListScreen_empty_light.png
app/src/test/java/com/yorvana/ui/vehicles/VehicleListViewModelTest.kt
app/src/test/java/com/yorvana/ui/vehicles/VehicleListScreenshotTest.kt
app/src/test/java/com/yorvana/ui/vehicles/VehicleListScreenIT.kt
app/src/test/java/com/yorvana/ui/vehicles/VehicleListConsentDialogTest.kt
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleViewModelTest.kt
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleViewModelIT.kt
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenshotTest.kt
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenIT.kt
app/src/androidTest/java/com/yorvana/regression/vault/StorageRobustnessRegressionTest.kt
app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt
app/src/androidTest/java/com/yorvana/testsupport/BillingSandboxEnvironment.kt
app/src/androidTest/java/com/yorvana/regression/vehicles/VehicleRegressionTest.kt
app/src/test/java/com/yorvana/ui/util/EmailLaunchRequestTest.kt
app/src/test/java/com/yorvana/ui/util/ViewModelFactoryTest.kt
app/src/test/java/com/yorvana/ui/util/ViewModelDependenciesExt.kt
app/src/test/java/com/yorvana/ui/util/ContextExtensionsTest.kt
app/src/test/java/com/yorvana/ui/util/BaseScreenshotTest.kt
app/src/androidTest/java/com/yorvana/testsupport/lifecycle/HardProcessRelaunch.kt
app/src/androidTest/java/com/yorvana/testsupport/lifecycle/TrimMemory.kt
app/src/androidTest/java/com/yorvana/testsupport/lifecycle/ProcessDeath.kt
app/src/androidTest/java/com/yorvana/testsupport/lifecycle/LauncherReentry.kt
app/src/main/java/com/yorvana/domain/model/VehicleWithMeta.kt
app/src/main/java/com/yorvana/domain/model/RecordWithCategory.kt
app/src/main/java/com/yorvana/domain/AppGate.kt
app/src/androidTest/java/com/yorvana/regression/settings/SettingsCategoriesPaywallRegressionTest.kt
app/src/androidTest/java/com/yorvana/testsupport/gestures/Gestures.kt
app/src/androidTest/java/com/yorvana/testsupport/VaultTestSetup.kt
app/src/androidTest/java/com/yorvana/testsupport/RegressionHelpers.kt
app/src/androidTest/java/com/yorvana/testsupport/Camera.kt
app/src/androidTest/java/com/yorvana/testsupport/HashHelpers.kt
app/src/androidTest/java/com/yorvana/testsupport/VaultFixture.kt
app/src/androidTest/java/com/yorvana/testsupport/ExternalViewer.kt
app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt
app/src/androidTest/java/com/yorvana/regression/records/RecordRegressionTest.kt
app/src/test/java/com/yorvana/ui/settings/SettingsScreenshotTest.kt
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenTest.kt
app/src/test/java/com/yorvana/ui/settings/SettingsViewModelTest.kt
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt
app/src/androidTest/java/com/yorvana/testsupport/tiers/Smoke.kt
app/src/androidTest/java/com/yorvana/testsupport/tiers/RegressionFull.kt
app/src/androidTest/java/com/yorvana/testsupport/tiers/Regression.kt
app/src/androidTest/java/com/yorvana/testsupport/tiers/Quarantined.kt
app/src/androidTest/java/com/yorvana/testsupport/tiers/BillingSandbox.kt
app/src/androidTest/java/com/yorvana/testsupport/SystemPicker.kt
app/src/androidTest/java/com/yorvana/testsupport/Permissions.kt
app/src/androidTest/java/com/yorvana/testsupport/FixtureAssetsRule.kt
app/src/androidTest/java/com/yorvana/testsupport/DemoMode.kt
app/src/androidTest/java/com/yorvana/testsupport/ScreenshotConstants.kt
app/src/androidTest/java/com/yorvana/testsupport/ComposeWaitHelpers.kt
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotBase.kt
app/src/main/java/com/yorvana/data/storage/VaultStorageImpl.kt
app/src/main/java/com/yorvana/data/storage/VaultStorage.kt
app/src/main/java/com/yorvana/data/storage/MigrationHelper.kt
app/src/test/java/com/yorvana/TestYorvanaApplication.kt
app/src/test/java/com/yorvana/MainActivityTest.kt
app/src/test/java/com/yorvana/LocalOnly.kt
app/src/test/java/com/yorvana/ui/components/RequiredLabelTest.kt
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_privacy_section_light.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_crash_reporting_on_light.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_premium_dark.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_privacy_dialog.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_debug_crash_section_dark.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_conflict_light.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_dark.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_existing_vault_light.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_premium_light.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_validating_light.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_light.png
app/src/test/java/com/yorvana/ui/settings/snapshots/SettingsScreen_debug_crash_section_light.png
app/src/main/java/com/yorvana/data/repository/VehicleRepositoryImpl.kt
app/src/test/java/com/yorvana/ui/settings/snapshots/CategoriesScreen_add_dialog.png
app/src/test/java/com/yorvana/ui/settings/snapshots/CategoriesScreen_light.png
app/src/main/java/com/yorvana/data/repository/RecordRepositoryImpl.kt
app/src/test/java/com/yorvana/ui/settings/snapshots/CategoriesScreen_dark.png
app/src/main/java/com/yorvana/data/repository/CategoryRepositoryImpl.kt
app/src/main/java/com/yorvana/data/repository/VehicleRepository.kt
app/src/main/java/com/yorvana/data/repository/RecordRepository.kt
app/src/main/java/com/yorvana/data/repository/CategoryRepository.kt
app/src/test/java/com/yorvana/ui/settings/CategoriesViewModelTest.kt
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenshotTest.kt
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenIT.kt
app/src/test/java/com/yorvana/data/storage/VaultStorageImplIT.kt
app/src/test/java/com/yorvana/data/storage/VaultStorageImplTest.kt
app/src/test/java/com/yorvana/data/storage/MigrationHelperTest.kt
app/src/main/java/com/yorvana/data/preferences/AppPreferencesImpl.kt
app/src/main/java/com/yorvana/data/preferences/AppPreferences.kt
app/src/main/java/com/yorvana/data/preferences/DebugBillingOverrideMode.kt
app/src/test/java/com/yorvana/ui/components/snapshots/UpgradeDialog_light.png
app/src/test/java/com/yorvana/ui/components/snapshots/UpgradeDialog_dark.png
app/src/test/java/com/yorvana/ui/components/snapshots/PaywallStatusCard_pending_dark.png
app/src/test/java/com/yorvana/ui/components/snapshots/PaywallStatusCard_pending_light.png
app/src/test/java/com/yorvana/ui/components/snapshots/PaywallStatusCard_readOnly_light.png
app/src/test/java/com/yorvana/ui/components/snapshots/PaywallStatusCard_readOnly_dark.png
app/src/test/java/com/yorvana/ui/components/snapshots/PaywallInfoSheet_pending_dark.png
app/src/test/java/com/yorvana/ui/components/snapshots/PaywallInfoSheet_readOnly_dark.png
app/src/test/java/com/yorvana/ui/components/snapshots/PaywallScaffold_noSheet_dark.png
app/src/test/java/com/yorvana/data/repository/WorkflowIntegrationIT.kt
app/src/test/java/com/yorvana/data/repository/VehicleRepositoryImplTest.kt
app/src/test/java/com/yorvana/data/repository/RecordRepositoryImplTest.kt
app/src/test/java/com/yorvana/data/repository/DataIntegrationIT.kt
app/src/test/java/com/yorvana/data/repository/CategoryRepositoryImplTest.kt
app/src/test/java/com/yorvana/ui/components/snapshots/PaywallInfoSheet_readOnly_light.png
app/src/test/java/com/yorvana/ui/components/snapshots/PaywallScaffold_noSheet_light.png
app/src/test/java/com/yorvana/ui/components/snapshots/EmptyStateView_light.png
app/src/test/java/com/yorvana/ui/components/snapshots/RequiredLabel.png
app/src/test/java/com/yorvana/ui/components/snapshots/PaywallInfoSheet_pending_light.png
app/src/test/java/com/yorvana/ui/components/snapshots/ErrorListItem_light.png
app/src/test/java/com/yorvana/ui/components/snapshots/AttachmentThumbnail_noUri.png
app/src/test/java/com/yorvana/ui/components/snapshots/CustomCurrencyDialog.png
app/src/test/java/com/yorvana/ui/components/snapshots/ConfirmDeleteDialog.png
app/src/test/java/com/yorvana/ui/components/snapshots/ChromeLikeMenu.png
app/src/test/java/com/yorvana/ui/components/snapshots/AttachmentChip_deletable.png
app/src/test/java/com/yorvana/ui/components/snapshots/EmptyStateView_dark.png
app/src/test/java/com/yorvana/ui/components/snapshots/RequiredLabel_default.png
app/src/test/java/com/yorvana/ui/components/snapshots/CurrencyPickerDialog.png
app/src/test/java/com/yorvana/ui/components/snapshots/AttachmentChip_readOnly.png
app/src/test/java/com/yorvana/ui/components/snapshots/MakePickerDialog.png
app/src/test/java/com/yorvana/ui/components/UpgradeDialogTest.kt
app/src/test/java/com/yorvana/ui/components/UpgradeDialogScreenshotTest.kt
app/src/test/java/com/yorvana/ui/components/PaywallStatusCardScreenshotTest.kt
app/src/test/java/com/yorvana/ui/components/PaywallScaffoldScreenshotTest.kt
app/src/test/java/com/yorvana/ui/components/PaywallInfoSheetScreenshotTest.kt
app/src/test/java/com/yorvana/ui/components/ComponentScreenshotTest.kt
app/src/test/java/com/yorvana/ui/reportbug/snapshots/ReportBugScreen_disclosure_light.png
app/src/test/java/com/yorvana/ui/reportbug/snapshots/ReportBugScreen_device_info_dialog_light.png
app/src/test/java/com/yorvana/ui/reportbug/snapshots/ReportBugScreen_filled_dark.png
app/src/test/java/com/yorvana/ui/reportbug/snapshots/ReportBugScreen_empty_light.png
app/src/test/java/com/yorvana/ui/reportbug/snapshots/ReportBugScreen_submitting_light.png
app/src/test/java/com/yorvana/ui/reportbug/snapshots/ReportBugScreen_invalid_email_dark.png
app/src/test/java/com/yorvana/ui/reportbug/snapshots/ReportBugScreen_invalid_email_light.png
app/src/test/java/com/yorvana/ui/reportbug/ReportBugViewModelTest.kt
app/src/test/java/com/yorvana/ui/reportbug/ReportBugScreenshotTest.kt
app/src/test/java/com/yorvana/ui/reportbug/ReportBugScreenTest.kt
app/src/test/java/com/yorvana/data/preferences/AppPreferencesImplTest.kt
app/src/main/java/com/yorvana/data/model/Vehicle.kt
app/src/main/java/com/yorvana/data/model/Category.kt
app/src/main/java/com/yorvana/data/model/WorkItem.kt
app/src/main/java/com/yorvana/data/model/ServiceRecord.kt
app/src/main/java/com/yorvana/data/model/AttachmentMeta.kt
app/src/test/java/com/yorvana/domain/AppGateTest.kt
app/src/test/java/com/yorvana/data/billing/BillingManagerImplTest.kt
app/src/test/java/com/yorvana/ui/records/RecordDetailViewModelTest.kt
app/src/test/java/com/yorvana/data/billing/FakeBillingManager.kt
app/src/test/java/com/yorvana/YorvanaApplicationTest.kt
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenshotTest.kt
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenTest.kt
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenIT.kt
app/src/test/java/com/yorvana/ui/records/RecordDetailScreenshotTest.kt
app/src/test/java/com/yorvana/ui/records/RecordDetailScreenIT.kt
app/src/test/java/com/yorvana/ui/records/ImageViewerScreenshotTest.kt
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenshotTest.kt
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt
app/src/test/java/com/yorvana/ui/records/RecordDetailViewModelIT.kt
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenTest.kt
app/src/main/java/com/yorvana/data/billing/DebugBillingActions.kt
app/src/main/java/com/yorvana/data/billing/BillingManager.kt
app/src/main/AndroidManifest.xml
app/src/main/java/com/yorvana/data/billing/BillingResults.kt
app/src/test/java/com/yorvana/ui/navigation/NavRouteCodecTest.kt
app/src/main/java/com/yorvana/data/billing/BillingManagerImpl.kt
app/src/main/java/com/yorvana/data/billing/BillingClientProvider.kt
app/src/main/java/com/yorvana/data/billing/DebugBillingOverride.kt
app/src/test/java/com/yorvana/ui/records/VehicleDetailViewModelTest.kt
app/src/test/java/com/yorvana/ui/navigation/SetupScreenTest.kt
app/src/test/java/com/yorvana/ui/navigation/SetupScreenshotTest.kt
app/src/test/java/com/yorvana/ui/navigation/snapshots/SetupScreen_step2_sentry_privacy_dialog.png
app/src/test/java/com/yorvana/ui/navigation/snapshots/SetupScreen_step2_with_topbar.png
app/src/test/java/com/yorvana/ui/navigation/snapshots/SetupScreen_step2_sentry_off.png
app/src/test/java/com/yorvana/ui/navigation/snapshots/SetupScreen_step1_folder.png
app/src/test/java/com/yorvana/ui/navigation/snapshots/SetupScreen_step2_sentry_on.png
app/src/test/java/com/yorvana/ui/records/snapshots/VehicleDetailScreen_light.png
app/src/test/java/com/yorvana/ui/records/snapshots/VehicleDetailScreen_menu_dark.png
app/src/test/java/com/yorvana/ui/records/snapshots/VehicleDetailScreen_menu_light.png
app/src/test/java/com/yorvana/ui/records/snapshots/VehicleDetailScreen_dark.png
app/src/test/java/com/yorvana/ui/records/snapshots/RecordDetailScreen_dark.png
app/src/test/java/com/yorvana/ui/records/snapshots/RecordDetailScreen_light.png
app/src/test/java/com/yorvana/ui/records/snapshots/ImageViewerScreen.png
app/src/test/java/com/yorvana/ui/records/snapshots/AddEditRecordScreen_add_light.png
app/src/test/java/com/yorvana/ui/records/snapshots/AddEditRecordScreen_custom_task_dialog.png
app/src/test/java/com/yorvana/ui/records/snapshots/AddEditRecordScreen_add_dark.png
app/src/test/java/com/yorvana/ui/records/snapshots/AddEditRecordScreen_edit_dark.png
app/src/test/java/com/yorvana/ui/records/snapshots/AddEditRecordScreen_edit_light.png
app/src/main/res/values-night/themes.xml
app/src/main/res/xml/file_provider_paths.xml
app/src/main/res/values/strings.xml
app/src/main/res/values/themes.xml
app/src/main/res/values/colors.xml
app/src/main/res/drawable/ic_launcher_monochrome.xml
app/src/main/res/drawable/ic_launcher_foreground.xml
app/src/main/res/drawable/ic_launcher_background.xml
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml


I have the repo inventory and memory pointers. The issue lookup is still returning, so I’m also checking the regression plan and existing Android regression tests to see where #119 fits in the current suite.


rg -n "Phase 6|phase 6|#119|119|Performance|benchmark|jank|large|stress|perf" docs/plan-regression-tests.md app/src/androidTest/java app/src/test/java TESTING_SETUP.md app/build.gradle.kts


sed -n '1,260p' docs/plan-regression-tests.md


find app/src/androidTest/java/com/yorvana/regression -type f -maxdepth 4


Output:
Chunk ID: 8eefed
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 6676
Output:
{"body":"Tracks **Phase 6** of [`docs/plan-regression-tests.md`](docs/plan-regression-tests.md).\n\n**Blocked by #114** (Phase 1). Independent of Phases 2–5.\n\n## Scope\n\nA new `:macrobenchmark` Gradle module measuring NFR-1 / NFR-6 with `androidx.benchmark`. Numbers from a swiftshader/angle emulator are advisory regression signal only — authoritative perf measurements still require a physical-device run outside the nightly aggregate.\n\n## Prerequisites (must be in place before benchmarks run)\n\nThese four pieces of plumbing are part of Phase 6 — they cannot be deferred without leaving the benchmarks fragile or unmeasurable.\n\n### A. Production trace sections for M-04\n\n`AppNavGraph` uses Compose `NavDisplay` animations with **no custom `trace {}` sections**, so `TraceSectionMetric` has nothing stable to attach to today. Pick one:\n\n- **Preferred — add `androidx.tracing` sections** in production around the garage → detail → record path (e.g. `trace(\"nav.garage_to_detail\") { ... }` and `trace(\"nav.detail_to_record\") { ... }`). **Span contract: the trace section must span the actual transition work being measured — destination composition + transition animation frames — not the synchronous navigation event** (click handler, back-stack mutation, `NavController` call). Wrapping only the event would make `TraceSectionMetric` report the synchronous command latency, not the perceived transition, and the benchmark would look green while measuring the wrong thing. If a reliable span around Navigation 3 / `NavDisplay` animations cannot be established at the transition-work boundary, **fall back to the alternative below** rather than narrowing the span. These trace points are no-ops in non-traced runs and cheap enough to keep in release. M-04 then asserts against those stable section names.\n- **Alternative — switch M-04's metric.** Drop `TraceSectionMetric` in favour of a `FrameTimingMetric` over a scripted nav journey (or `StartupTimingMetric`-style observation of the destination composition), so no production instrumentation is required. Document the choice in the module README.\n\nDo **not** ship `TraceSectionMetric` without one of these — the benchmark will either no-op or measure the wrong span. Specifically, `TraceSectionMetric` against a section that wraps only the synchronous nav event is treated as the same failure mode as a non-existent section.\n\n### B. Stable test tags for scroll/nav targets\n\nVehicle cards have no per-vehicle test tag, and the vehicle/record scroll containers themselves are untagged. Macrobenchmark can fall back to text selectors, but that makes fixture naming part of the test contract. Before M-02 / M-03 / M-04 are wired, add to `app/src/main/java/com/yorvana/ui/TestTags.kt`:\n\n- `VEHICLE_LIST` — `LazyColumn`/`LazyVerticalGrid` container in `VehicleListScreen`.\n- `fun vehicleItem(vehicleId: String)` — per-row tag on the vehicle card, parallel to the existing `recordItem(recordId)`.\n- `RECORD_LIST` — scroll container in `VehicleDetailScreen` (record list).\n\nMacrobenchmark scenarios drive scrolls/nav against these tags via `By.res(...)`; absence forces brittle text matching.\n\n**Required for `By.res(...)` to resolve Compose tags.** A plain `Modifier.testTag(...)` is **not** automatically visible to UiAutomator as a resource id — `By.res(TestTags.VEHICLE_LIST)` will silently never find the node, so the Compose-test side passes while the macrobenchmark side no-ops. Phase 6 must apply `Modifier.semantics { testTagsAsResourceId = true }` to the Compose hierarchy used by macrobenchmark — at the app root (or scoped to the subtree that hosts the tagged surfaces). Document the placement in the module README so a future reviewer doesn't strip it as dead code.\n\n### C. Cross-module fixture ownership\n\n`VaultTestSetup.installFreshVault()` lives in `app/src/androidTest` and **cannot be referenced from `:macrobenchmark`**. Phase 6 must pick **one** ownership strategy in the PR:\n\n- **(a) Shared test fixtures module.** Extract the deterministic-vault-seeding helper into a new `:testfixtures` (or `:app:testfixtures`) module that both `app/androidTest` and `:macrobenchmark/androidTest` consume. Cleanest long-term; biggest diff.\n- **(b) Benchmark-variant-only app test hook.** Add a `ContentProvider` or `ActivityAction` exposed by `:app` that the macrobenchmark target-app process can invoke (`UiDevice.executeShellCommand(\"am start -a com.yorvana.test.SEED_VAULT --es vault large_20v_200r\")`). **Must not be gated on `BuildConfig.DEBUG`** — Prerequisite D requires the benchmark target to use a non-debuggable `benchmark` variant where `BuildConfig.DEBUG` is `false`, so a debug-gated hook would not exist when the benchmark calls it. Gate the hook on the benchmark build type instead: source-set-specific provider/activity-alias under `:app/src/benchmark/`, a manifest placeholder injected only by the `benchmark` variant, or an explicit `buildConfigField(\"Boolean\", \"BENCHMARK\", \"true\")` on the `benchmark` build type. The contract is: present in the macrobenchmark target variant, absent from `release`/`staging` user builds, independent of `BuildConfig.DEBUG`.\n- **(c) Intentional duplication.** Copy the seeding helper into `:macrobenchmark/androidTest/...` and call out in the README that the two copies must be kept in sync. Smallest diff; highest drift risk.\n\nThe PR must state which strategy is chosen and why; the seeding helper must produce **deterministic** fixtures (20 vehicles for M-02, 200 records on a single vehicle for M-03) that land the app on the garage screen with setup skipped before each benchmark iteration.\n\n### D. Benchmark build variant\n\nA plain `com.android.test` module + dependency on `benchmark-macro-junit4` is not enough — it will accidentally benchmark a debug build or lose JSON results in CI. Phase 6 must:\n\n- Configure the module to **target `:app`** via `targetProjectPath = \":app\"`.\n- Add a dedicated `benchmark` build type in `:app` (and in `:macrobenchmark`) with `debuggable = false`, `signingConfig = signingConfigs.debug`, `matchingFallbacks = listOf(\"release\")`, and `<profileable shell=\"true\" />` injected into `:app`'s `AndroidManifest.xml` via a manifest placeholder or a benchmark-only manifest. Macrobenchmark requires a non-debuggable + profileable variant; debug builds give noisy/invalid numbers.\n- Configure `androidx.benchmark.suppressErrors` only for known-acceptable conditions (e.g. `EMULATOR` on the swiftshader runner). Do **not** blanket-suppress; the suppression list is the public record of \"these numbers are advisory, here's why.\"\n- Ensure the macrobenchmark JSON output (`additionalTestOutputDir`) is uploaded as a CI artifact on every run (success or failure) so calibration drift is visible without re-running.\n\n## Deliverables\n\n### Module setup\n- [ ] New `:macrobenchmark` module with the `com.android.test` plugin and `androidx.benchmark:benchmark-macro-junit4` dependency.\n- [ ] `targetProjectPath = \":app\"`, dedicated `benchmark` build type per [Prerequisite D](#d-benchmark-build-variant), `<profileable>` enabled on the target app variant.\n- [ ] Register a dedicated GMD entry inside `:macrobenchmark` — a Pixel 2 API 33 device matching the app module's GMD shape — so AGP generates `:macrobenchmark:pixel2api33BenchmarkAndroidTest`.\n- [ ] Wire that exact task into `regressionFullCheck`'s `dependsOn` **as a normal sibling task, outside any `GradleBuild` child invocations Phase 1 introduces.** App-module instrumentation arguments (`annotation=...`, `notAnnotation=...`) must not leak into the benchmark run; if Phase 1 ships a filtered child invocation, the benchmark task is added at the same level as that child, not inside it.\n- [ ] Do **not** depend on `:macrobenchmark:connectedBenchmarkAndroidTest` (targets ad-hoc connected devices CI doesn't have).\n- [ ] Macrobenchmark JSON output uploaded as a CI artifact on every run.\n- [ ] README in the module documenting the \"swiftshader numbers are advisory\" caveat **plus** the chosen fixture-ownership strategy from [Prerequisite C](#c-cross-module-fixture-ownership) and the M-04 instrumentation choice from [Prerequisite A](#a-production-trace-sections-for-m-04).\n\n### Benchmarks (§3.9)\n- [ ] M-01 — cold start (`StartupTimingMetric`); P50 ≤ 2000ms target (see [Threshold policy](#threshold-policy-fail-vs-warn) for fail/warn rules).\n- [ ] M-02 — vehicle list scroll FPS with 20 vehicles (`FrameTimingMetric`); drives scroll via `By.res(VEHICLE_LIST)`.\n- [ ] M-03 — record list scroll FPS with 200 records on a single vehicle; drives scroll via `By.res(RECORD_LIST)`.\n- [ ] M-04 — navigation transition garage→detail→record. Metric per [Prerequisite A](#a-production-trace-sections-for-m-04) (either `TraceSectionMetric` against `nav.garage_to_detail` / `nav.detail_to_record`, or a `FrameTimingMetric` over a scripted journey).\n\n### Threshold policy (fail vs warn)\n\nBoth absolute NFRs and relative regression alerts coexist on advisory emulator numbers; the policy must be explicit and documented in the module README:\n\n- [ ] **Hard-fail in CI** — relative regression > 20% against the committed baseline (5-run calibration on the same swiftshader GMD shape). This is the meaningful signal because runner-to-runner noise is constant.\n- [ ] **Advisory only (warn + artifact, never red-X CI)** — absolute NFR targets like M-01's P50 ≤ 2000ms. Swiftshader's absolute numbers are not authoritative, so hard-failing on them would chase emulator noise. The PR / nightly artifact must record whether the absolute threshold was hit, but only the relative regression gate fails the build.\n- [ ] Calibrate baselines over 5 runs on the GMD runner; commit them under `:macrobenchmark/baselines/` (or equivalent) and reference the 5-run calibration in the PR description.\n\n## Acceptance\n\n- [ ] `./gradlew :macrobenchmark:pixel2api33BenchmarkAndroidTest` runs all four benchmarks.\n- [ ] `./gradlew regressionFullCheck` triggers the benchmark task as a sibling dependency (no instrumentation-arg leakage from app-module child invocations).\n- [ ] M-04 ships either with production trace sections **that span the transition work (destination composition + animation frames), not just the synchronous nav event**, or with a non-trace metric (`FrameTimingMetric` over a scripted journey). `TraceSectionMetric` against a non-existent section OR against a section that wraps only the synchronous event is treated as the same failure mode and must not ship.\n- [ ] `TestTags.VEHICLE_LIST`, `TestTags.vehicleItem(id)`, `TestTags.RECORD_LIST` exist and are wired in the production composables they target.\n- [ ] `Modifier.semantics { testTagsAsResourceId = true }` is applied to the Compose hierarchy hosting those tags (app root or the relevant subtree), so `By.res(...)` in `:macrobenchmark` resolves them. Placement is documented in the module README.\n- [ ] Fixture-ownership strategy chosen and documented; deterministic 20-vehicle / 200-record vault seeding works across `app/androidTest` and `:macrobenchmark`.\n- [ ] Module uses a non-debuggable + profileable `benchmark` variant against `:app`; debug builds are not benchmarked.\n- [ ] Threshold policy (>20% regression hard-fails; absolute NFRs advisory) documented in module README and enforced in the benchmark assertions / CI config.\n- [ ] Baseline numbers committed with 5-run calibration noted in the PR.\n\n## References\n\n- Plan: [`docs/plan-regression-tests.md`](docs/plan-regression-tests.md) §3.9, §4 Phase 6, §2 (GMD aggregator shape)\n- Specs: [`docs/specs.md`](docs/specs.md) NFR-1, NFR-6\n- Code: `TestTags` (`app/src/main/java/com/yorvana/ui/TestTags.kt`), `VaultTestSetup.installFreshVault` (`app/src/androidTest/java/com/yorvana/testsupport/VaultTestSetup.kt`), `AppNavGraph` (`app/src/main/java/com/yorvana/ui/navigation/AppNavGraph.kt`)\n- Phase 1 infrastructure: #114\n\n\n\n","comments":[{"id":"IC_kwDOR2Hmq88AAAABDOfyRA","author":{"login":"Mesya82"},"authorAssociation":"MEMBER","body":"A few things I would adjust before this is implemented:\n\n1. `TraceSectionMetric` for M-04 needs production/test trace points, not just a benchmark test. The current nav transitions in `AppNavGraph` are Compose `NavDisplay` animations with no custom `trace {}` sections, so `TraceSectionMetric` will have nothing stable to measure unless Phase 6 also adds explicit trace sections around the garage -> detail -> record path, or switches M-04 to a metric Macrobenchmark can observe without production instrumentation.\n\n2. The plan should define benchmark data/setup more concretely. M-02 and M-03 need deterministic large vault fixtures installed before each benchmark run, plus app state that skips setup and lands on the garage screen. The existing `VaultTestSetup.installFreshVault()` helper is in `app/src/androidTest`, so a separate `:macrobenchmark` module cannot directly reuse it unless the setup logic is moved to shared test fixtures, duplicated intentionally, or exposed through a debug-only app test hook.\n\n3. Stable UI selectors are currently incomplete for macrobenchmark navigation. Record rows have `TestTags.recordItem(recordId)`, but vehicle cards do not have per-vehicle test tags, and the scroll containers themselves are not tagged. Macrobenchmarks can fall back to text selectors, but that makes fixture naming part of the test contract. I would add explicit tags for the vehicle list, vehicle rows, and record list before relying on scroll/nav benchmarks.\n\n4. The threshold language mixes an absolute NFR with relative regression alerting. M-01 says P50 <= 2000ms while the phase says alert on >20% regression after 5-run calibration. That is fine if both are intentional, but the plan should specify which metrics hard-fail in CI versus which only emit advisory warnings/artifacts, especially since the issue correctly says swiftshader emulator numbers are not authoritative.\n\n5. Module setup should call out the required benchmark build behavior: target `:app`, use a non-debuggable/profileable benchmarkable variant as appropriate for Macrobenchmark, and configure the benchmark runner/output artifacts. A plain `com.android.test` module plus dependency is not quite enough to avoid accidentally benchmarking a debug build or losing JSON results in CI.\n\n6. Wiring `:macrobenchmark:pixel2api33BenchmarkAndroidTest` into `regressionFullCheck` depends on Phase 1’s eventual aggregator shape. If `regressionFullCheck` uses `GradleBuild` child invocations for the app tiers, add the macrobenchmark task as a normal sibling dependency outside those filtered child invocations so app-module instrumentation arguments do not leak into the benchmark run.\n\nNet: I agree with adding Phase 6 here, and the dedicated GMD task requirement is the right correction. The main missing pieces are trace instrumentation, deterministic fixture/setup ownership across modules, stable selectors, and a clearer fail/warn threshold policy for emulator performance numbers.","createdAt":"2026-05-21T18:30:54Z","includesCreatedEdit":false,"isMinimized":false,"minimizedReason":"","reactionGroups":[],"url":"https://github.com/yorvana/android/issues/119#issuecomment-4511494724","viewerDidAuthor":true},{"id":"IC_kwDOR2Hmq88AAAABDOi67w","author":{"login":"Mesya82"},"authorAssociation":"MEMBER","body":"Updated the issue body to address the review. Six concrete changes, gathered under a new **Prerequisites** section so they can't be deferred without leaving the benchmarks fragile:\n\n1. **M-04 trace instrumentation (review #1)** — Added [Prerequisite A](#a-production-trace-sections-for-m-04): either ship explicit `trace(\"nav.garage_to_detail\") { ... }` / `trace(\"nav.detail_to_record\") { ... }` sections in `AppNavGraph` (preferred) **or** switch M-04 off `TraceSectionMetric` to `FrameTimingMetric` over a scripted nav journey. Acceptance forbids shipping `TraceSectionMetric` against non-existent sections.\n\n2. **Cross-module fixture ownership (review #2)** — Added [Prerequisite C](#c-cross-module-fixture-ownership). `VaultTestSetup.installFreshVault()` is in `app/src/androidTest` and cannot be referenced from `:macrobenchmark`. PR must pick one of: (a) extract to a `:testfixtures` shared module, (b) debug-only `ContentProvider`/`ActivityAction` invoked via `am start` from the benchmark process, or (c) intentional duplication with a sync-required note. Deterministic 20-vehicle / 200-record fixtures landing on garage required.\n\n3. **Stable UI selectors (review #3)** — Added [Prerequisite B](#b-stable-test-tags-for-scrollnav-targets). New `TestTags` entries required before any scroll/nav benchmark is wired: `VEHICLE_LIST`, `vehicleItem(vehicleId)`, `RECORD_LIST`. Scenarios drive scrolls via `By.res(...)` against these tags rather than fragile text selectors.\n\n4. **Threshold policy fail vs warn (review #4)** — Added a [Threshold policy](#threshold-policy-fail-vs-warn) section. **Hard-fail:** >20% relative regression vs committed baseline (this is the meaningful signal on swiftshader because runner-to-runner noise is constant). **Advisory only:** absolute NFRs like M-01 P50 ≤ 2000ms — recorded as an artifact, never a red-X — because swiftshader absolute numbers are not authoritative.\n\n5. **Benchmark build variant (review #5)** — Added [Prerequisite D](#d-benchmark-build-variant). Module must set `targetProjectPath = \":app\"`, ship a dedicated `benchmark` build type (`debuggable = false`, `<profileable shell=\"true\">`), suppress only known-acceptable errors (e.g. `EMULATOR`), and upload the macrobenchmark JSON output as a CI artifact on every run.\n\n6. **regressionFullCheck wiring (review #6)** — Tightened the `dependsOn` requirement: the benchmark task is wired as a **normal sibling**, outside any `GradleBuild` child invocations Phase 1 introduces, so app-module instrumentation arguments (`annotation` / `notAnnotation`) don't leak into the benchmark run.\n\nAlso added a References block linking the relevant code (`TestTags.kt`, `VaultTestSetup.kt`, `AppNavGraph.kt`) and Phase 1 (#114).\n","createdAt":"2026-05-21T18:34:29Z","includesCreatedEdit":false,"isMinimized":false,"minimizedReason":"","reactionGroups":[],"url":"https://github.com/yorvana/android/issues/119#issuecomment-4511546095","viewerDidAuthor":true},{"id":"IC_kwDOR2Hmq88AAAABDTca1g","author":{"login":"Mesya82"},"authorAssociation":"MEMBER","body":"One remaining implementation detail from the latest full sweep:\n\nThe macrobenchmark selector plan should explicitly enable Compose test tags as UiAutomator resource IDs. The issue requires `TestTags.VEHICLE_LIST`, `TestTags.vehicleItem(id)`, and `TestTags.RECORD_LIST`, then says the benchmark drives them via `By.res(...)`. A plain `Modifier.testTag(...)` is not automatically visible to UiAutomator as a resource id.\n\nPlease add a Phase 6 deliverable / acceptance check to put `Modifier.semantics { testTagsAsResourceId = true }` on the Compose hierarchy used by macrobenchmark, likely near the app root. Without that, the tags can work for Compose tests while `By.res(TestTags.VEHICLE_LIST)` in macrobenchmark never finds the node.\n","createdAt":"2026-05-22T08:00:20Z","includesCreatedEdit":false,"isMinimized":false,"minimizedReason":"","reactionGroups":[],"url":"https://github.com/yorvana/android/issues/119#issuecomment-4516682454","viewerDidAuthor":true},{"id":"IC_kwDOR2Hmq88AAAABDTdVfw","author":{"login":"Mesya82"},"authorAssociation":"MEMBER","body":"Tightened Phase 6 per the latest review note.\n\nCompose `Modifier.testTag(...)` is not automatically visible to UiAutomator as a resource id, so `By.res(TestTags.VEHICLE_LIST)` from `:macrobenchmark` would silently never find the node while the Compose-test side keeps passing — a green guardrail that no-ops. Two changes:\n\n1. **Prerequisite B (stable test tags)** now requires `Modifier.semantics { testTagsAsResourceId = true }` to be applied to the Compose hierarchy hosting the tagged surfaces — at the app root, or scoped to the subtree that contains `VEHICLE_LIST` / `vehicleItem(id)` / `RECORD_LIST`. Placement documented in the module README so a future reviewer doesn't strip it as dead code.\n\n2. **Acceptance** gains a matching checkbox so the wiring can't be deferred under \"the tags exist\" — the tags must be *resolvable from `:macrobenchmark`*, not just present.\n\nNo other deliverables changed.\n","createdAt":"2026-05-22T08:02:01Z","includesCreatedEdit":false,"isMinimized":false,"minimizedReason":"","reactionGroups":[],"url":"https://github.com/yorvana/android/issues/119#issuecomment-4516697471","viewerDidAuthor":true},{"id":"IC_kwDOR2Hmq88AAAABDTqlHQ","author":{"login":"Mesya82"},"authorAssociation":"MEMBER","body":"One remaining Phase 6 implementation contradiction from the full sweep:\n\nPrerequisite C option **(b)** suggests a debug-only app test hook guarded by `BuildConfig.DEBUG`, but Prerequisite D correctly requires the macrobenchmark target app to use a non-debuggable `benchmark` variant. In that variant `BuildConfig.DEBUG` will be `false`, so a `BuildConfig.DEBUG`-gated seed hook would not exist when the macrobenchmark needs to call it.\n\nPlease tighten option (b) to say the hook must be **benchmark-variant-only / test-only**, not `BuildConfig.DEBUG`-only. For example: expose it only from the `benchmark` build type via a manifest placeholder, source-set-specific provider/activity-alias, or an explicit `BuildConfig.BENCHMARK` flag. The important contract is:\n\n- present in the macrobenchmark target variant;\n- absent from release/staging user builds;\n- not dependent on `BuildConfig.DEBUG`, since the benchmark variant must stay non-debuggable/profileable.\n\nWithout this, option (b) looks selectable but would fail at runtime.\n","createdAt":"2026-05-22T08:21:21Z","includesCreatedEdit":false,"isMinimized":false,"minimizedReason":"","reactionGroups":[],"url":"https://github.com/yorvana/android/issues/119#issuecomment-4516914461","viewerDidAuthor":true},{"id":"IC_kwDOR2Hmq88AAAABDTrlXw","author":{"login":"Mesya82"},"authorAssociation":"MEMBER","body":"Tightened Prerequisite C option (b) per the latest review note.\n\nPrior wording gated the seed hook on `BuildConfig.DEBUG`, which contradicts Prerequisite D — the macrobenchmark target must run on the non-debuggable `benchmark` variant where `BuildConfig.DEBUG` is `false`, so a `DEBUG`-gated hook would not exist at the moment the benchmark tries to invoke it. Option (b) would have looked selectable in review and silently failed at runtime.\n\nReplaced with benchmark-variant-only gating. Acceptable shapes named explicitly: source-set-specific provider/activity-alias under `:app/src/benchmark/`, a manifest placeholder injected only by the `benchmark` variant, or an explicit `buildConfigField(\"Boolean\", \"BENCHMARK\", \"true\")` on the `benchmark` build type. The contract is spelled out: present in the macrobenchmark target variant, absent from `release`/`staging` user builds, **independent of `BuildConfig.DEBUG`** so it stays compatible with the non-debuggable/profileable requirement.\n\nNo other prerequisites or deliverables changed; options (a) and (c) are unaffected.\n","createdAt":"2026-05-22T08:23:04Z","includesCreatedEdit":false,"isMinimized":false,"minimizedReason":"","reactionGroups":[],"url":"https://github.com/yorvana/android/issues/119#issuecomment-4516930911","viewerDidAuthor":true},{"id":"IC_kwDOR2Hmq88AAAABDT0glg","author":{"login":"Mesya82"},"authorAssociation":"MEMBER","body":"Small tightening for **M-04 trace measurement**:\n\nThe issue allows adding `trace(\"nav.garage_to_detail\") { ... }` / `trace(\"nav.detail_to_record\") { ... }` \"at the navigation entry points\" in `AppNavGraph`. That wording is a little risky. If the trace only wraps the click handler or back-stack mutation, `TraceSectionMetric` will measure the synchronous navigation command, not the actual Compose transition frames.\n\nPlease make the contract explicit: if M-04 uses `TraceSectionMetric`, the trace section must span the transition work being measured, not just the event that requests navigation. If that cannot be made reliable around Navigation 3 / `NavDisplay` animations, choose the already-allowed `FrameTimingMetric` scripted nav journey instead.\n","createdAt":"2026-05-22T08:40:52Z","includesCreatedEdit":false,"isMinimized":false,"minimizedReason":"","reactionGroups":[],"url":"https://github.com/yorvana/android/issues/119#issuecomment-4517077142","viewerDidAuthor":true},{"id":"IC_kwDOR2Hmq88AAAABDT2XPA","author":{"login":"Mesya82"},"authorAssociation":"MEMBER","body":"Tightened **M-04** trace-section contract per the latest review note.\n\nPrior Prerequisite A wording said \"add `trace(...)` sections … at the navigation entry points in `AppNavGraph`.\" That left the span boundary ambiguous: a trace that wraps only the click handler / back-stack mutation / `NavController` call would make `TraceSectionMetric` report the synchronous command latency, not the perceived transition. The benchmark would go green while measuring the wrong thing — worse than failing, because the metric looks credible.\n\nMade the span contract explicit:\n\n- **The trace section must span the actual transition work being measured — destination composition + transition animation frames — not the synchronous nav event.** Wrapping only the event is called out as a measurement failure mode, not an acceptable shape.\n- If a reliable span around Navigation 3 / `NavDisplay` animations cannot be established at the transition-work boundary, the deliverable falls back to the existing alternative (`FrameTimingMetric` over a scripted journey) rather than narrowing the span. Narrowing the span to \"what we can reliably wrap\" is explicitly not an option.\n- Acceptance checkbox updated to match: `TraceSectionMetric` against a section that wraps only the synchronous event is treated as **the same failure mode** as a non-existent section.\n\nNo other prerequisites or deliverables changed; the `FrameTimingMetric` alternative is unmodified.\n","createdAt":"2026-05-22T08:44:58Z","includesCreatedEdit":false,"isMinimized":false,"minimizedReason":"","reactionGroups":[],"url":"https://github.com/yorvana/android/issues/119#issuecomment-4517107516","viewerDidAuthor":true}],"labels":[{"id":"LA_kwDOR2Hmq88AAAACdjEaUw","name":"enhancement","description":"New feature or request","color":"a2eeef"}],"number":119,"state":"OPEN","title":"Regression tests — Phase 6: Macrobenchmark module (startup, scroll, nav)","url":"https://github.com/yorvana/android/issues/119"}


Output:
Chunk ID: 59e1ce
Wall time: 0.3204 seconds
Process exited with code 0
Original token count: 14399
Output:
Total output lines: 345

docs/plan-regression-tests.md:11:- **Catch real-device bugs before release** — SAF, camera, FileProvider, billing, lifecycle, perf. Things JVM/Robolectric structurally can't catch.
docs/plan-regression-tests.md:27:| Performance NFRs | **Macrobenchmark** (`androidx.benchmark`) in a separate module, running on its own dedicated GMD entry registered inside `:macrobenchmark` (a Pixel 2 API 33 entry matching the app's GMD, generating a task like `pixel2api33BenchmarkAndroidTest`). Not the app's smoke `pixel2api33` device, and not `connected*AndroidTest` (which would target ad-hoc connected devices, which CI doesn't have). Numbers from a swiftshader/angle emulator are advisory regression signal, not authoritative perf measurements. | new `:macrobenchmark` module |
docs/plan-regression-tests.md:28:| Fixture data | Bundle only a **minimal** asset set (one small PDF in Phase 1; JPG follows in Phase 2 if needed) in `app/src/androidTest/assets/`. Per #114 deliverable 6, large datasets (20 vehicles, 200 records, attachment-heavy vaults) are **generated in-process** by Phase 2/4/6 helpers rather than checked in as snapshots, so the repo doesn't carry binary asset bloat. | bundled in `app/src/androidTest/assets/` and copied to a known device path in `@Before` (matches smoke plan's recommendation; self-contained on GMD, no `adb` round-trip) |
docs/plan-regression-tests.md:42:| Macrobenchmark | separate module | Nightly | ~15 min |
docs/plan-regression-tests.md:47:- **`regressionFullCheck`** — Nightly. Aggregator task that depends on the sibling tasks below, plus eventually the macrobenchmark module. It does *not* `dependsOn(regressionCheck)`, because that task's `notAnnotation=...Quarantined` filter would otherwise hide quarantined core tests from nightly too (contradicting §1's quarantine policy). **Phase 1 (#114) wires only the first two siblings**. Phase 5 (#118) chose the external-device billing path, so the sandbox tier runs in `billing-sandbox.yml` rather than under `regressionFullCheck`; the `:macrobenchmark` `dependsOn` is deferred to **Phase 6 (#119)**.
docs/plan-regression-tests.md:55:  - None of the three set `notAnnotation`. The `:macrobenchmark` module's GMD-generated benchmark task (e.g. `:macrobenchmark:pixel2api33BenchmarkAndroidTest`, from the dedicated GMD entry registered in `:macrobenchmark` per Phase 6 and the §2 table) is wired as an additional `dependsOn` to keep "core + full + sandbox + benchmark" in one entry point. It is deliberately *not* `:macrobenchmark:connectedBenchmarkAndroidTest` — `connected*AndroidTest` targets ad-hoc currently-connected devices that CI doesn't have, so the nightly aggregate could otherwise pass without ever running the benchmark tier. (Separate tiers rather than a comma list because AndroidJUnitRunner's `annotation` argument takes a single class, and the sandbox tier needs a different device target anyway.)
docs/plan-regression-tests.md:76:- **R-R03** — 200-record list **correctness only**: scroll reaches first and last records, item order is stable, no crash or state loss. Frame-timing / jank measurement belongs to **M-03** (Phase 6 #119) — no `FrameTimingMetric` / Macrobenchmark code lands in Phase 2.
docs/plan-regression-tests.md:145:### 3.9 NFRs (NFR-1, NFR-6) — Macrobenchmark module
docs/plan-regression-tests.md:150:- **M-04** — Navigation transition duration garage→detail→record. Either `TraceSectionMetric` against production `androidx.tracing` sections (`nav.garage_to_detail`, `nav.detail_to_record`) whose **span covers the destination composition + transition animation frames, not just the synchronous nav event** (wrapping only the click handler / back-stack mutation would measure command latency, not perceived transition), **or** a `FrameTimingMetric` over a scripted nav journey if a reliable transition-work span can't be established. Phase 6 (#119) picks one and documents the choice in the module README.
docs/plan-regression-tests.md:157:- Wire `regressionCheck` and `regressionFullCheck` Gradle tasks per §2 using a **single chosen mechanism** (either `GradleBuild` child invocations with `startParameter.projectProperties`, or variant/device-scoped tasks with baked-in `instrumentationRunnerArguments`) — same shape for both, documented in the PR description. `regressionFullCheck` in Phase 1 wires **two siblings** (`regressionFullCoreCheck`, `regressionFullExtendedCheck`); `regressionFullSandboxCheck` is deferred to Phase 5 and the `:macrobenchmark` dependency to Phase 6.
docs/plan-regression-tests.md:161:- Bundle a **minimal** fixture set (one small PDF in Phase 1; JPG follows in Phase 2 if needed) in `app/src/androidTest/assets/` and the `@Before` / `TestRule` helper that copies it to the device-side location (Downloads via `MediaStore` for picker-visible files, app cache for app-internal reads). No large-vault snapshots — later phases generate large datasets in-process.
docs/plan-regression-tests.md:196:### Phase 6 — Macrobenchmark module (2 days) — issue **#119**
docs/plan-regression-tests.md:198:- New `:macrobenchmark` module (`com.android.test` + `androidx.benchmark:benchmark-macro-junit4`), M-01..04. Baseline thresholds calibrated over 5 runs; alert on >20% regression.
docs/plan-regression-tests.md:199:- Register a dedicated GMD entry inside `:macrobenchmark` so AGP generates `:macrobenchmark:pixel2api33BenchmarkAndroidTest`; wire that exact task into `regressionFullCheck`'s `dependsOn` **as a sibling outside any Phase 1 `GradleBuild` child invocation** so app-module instrumentation args don't leak into the benchmark run. Do **not** depend on `:macrobenchmark:connectedBenchmarkAndroidTest`.
docs/plan-regression-tests.md:200:- **Prerequisites that must land before benchmarks are meaningful:**
docs/plan-regression-tests.md:202:  - **B. Stable test tags.** `TestTags.VEHICLE_LIST`, `TestTags.vehicleItem(id)`, `TestTags.RECORD_LIST` wired in production composables, plus `Modifier.semantics { testTagsAsResourceId = true }` applied to the Compose hierarchy hosting them so `By.res(...)` in `:macrobenchmark` resolves.
docs/plan-regression-tests.md:203:  - **C. Cross-module fixture ownership.** Either (a) extract seeding into a shared `:testfixtures` module, (b) expose a benchmark-variant-only test hook (`ContentProvider` / `ActivityAction`) **not gated on `BuildConfig.DEBUG`** (the benchmark variant is non-debuggable; gate on the `benchmark` build type instead), or (c) intentional duplication with sync requirement.
docs/plan-regression-tests.md:204:  - **D. Benchmark build variant.** `targetProjectPath = ":app"`, dedicated `benchmark` build type (`debuggable = false`, `signingConfig = signingConfigs.debug`, `matchingFallbacks = listOf("release")`, `<profileable shell="true" />` on `:app`'s manifest); `androidx.benchmark.suppressErrors` only for known-acceptable conditions (e.g. `EMULATOR`); macrobenchmark JSON output uploaded as a CI artifact on every run.
docs/plan-regression-tests.md:205:- **Threshold policy** documented in the module README and enforced: **hard-fail** on relative regression > 20% against a committed 5-run baseline; absolute NFR targets (e.g. M-01 P50 ≤ 2000ms) are **advisory only** (warn + artifact, never red-X CI). Swiftshader absolute numbers are not authoritative; authoritative perf still requires a physical-device run outside this aggregate.
docs/plan-regression-tests.md:212:- **Flake-rate semantics** documented explicitly (passed-on-retry counts as flake; `Assume.assumeTrue` skips excluded from denominator; `@Ignore` excluded entirely; infrastructure failures recorded separately; benchmark failures categorised separately; cancellations excluded). Applied in the aggregator math.
app/build.gradle.kts:360:    description = "Runs Phase 1 full regression checks; sandbox and macrobenchmark siblings are deferred."
TESTING_SETUP.md:119:CI uses `swiftshader_indirect` for software rendering because GitHub-hosted runners lack a physical GPU. Local executions will default to the `host` GPU (specified in `gradle.properties`) for maximum performance, but can be overridden using the same `-P` flag if hardware acceleration is unavailable.
TESTING_SETUP.md:192:The Smoke Test suite (`app/src/androidTest/java/com/yorvana/SmokeTest.kt`) performs end-to-end verification of critical happy-path journeys on a real Android OS using Gradle Managed Devices (GMD).
app/src/androidTest/java/com/yorvana/regression/billing/BillingSandboxRegressionTest.kt:8:import androidx.compose.ui.test.performClick
app/src/androidTest/java/com/yorvana/regression/billing/BillingSandboxRegressionTest.kt:9:import androidx.compose.ui.test.performScrollTo
app/src/androidTest/java/com/yorvana/regression/billing/BillingSandboxRegressionTest.kt:61:        composeRule.onNodeWithTag(TestTags.ADD_VEHICLE_FAB).performClick()
app/src/androidTest/java/com/yorvana/regression/billing/BillingSandboxRegressionTest.kt:63:        composeRule.onNodeWithText(composeRule.activity.getString(R.string.paywall_sheet_cta)).performClick()
app/src/androidTest/java/com/yorvana/regression/billing/BillingSandboxRegressionTest.kt:99:            .performScrollTo()
app/src/androidTest/java/com/yorvana/regression/billing/BillingSandboxRegressionTest.kt:100:            .performClick()
app/src/androidTest/java/com/yorvana/regression/billing/BillingSandboxRegressionTest.kt:105:            .performScrollTo()
app/src/androidTest/java/com/yorvana/regression/billing/BillingSandboxRegressionTest.kt:110:        composeRule.onNodeWithTag(TestTags.SETTINGS_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:7:import androidx.compose.ui.test.performClick
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:8:import androidx.compose.ui.test.performTextInput
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:62:        composeRule.onNodeWithTag(TestTags.ADD_VEHICLE_FAB).performClick()
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:63:        composeRule.onNodeWithTag(TestTags.VEHICLE_NICKNAME_FIELD).performTextInput("Rotation Draft")
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:64:        composeRule.onNodeWithTag(TestTags.VEHICLE_MODEL_FIELD).performTextInput("Unsaved Model")
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:82:        composeRule.onNodeWithTag(TestTags.vehicleItem(vehicle.id)).performClick()
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:84:        composeRule.onNodeWithTag(TestTags.ADD_RECORD_FAB).performClick()
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:86:        composeRule.onNodeWithTag(TestTags.RECORD_ODOMETER_FIELD).performTextInput("45678")
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:87:        composeRule.onNodeWithTag(TestTags.PERFORMER_FIELD).performTextInput("Rotation Shop")
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:106:        val record = composeRule.seedRecord(vehicleId = vehicle.id, id = "kill-record", odometer = 32109, performer = "Persisted Shop")
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:107:        composeRule.onNodeWithTag(TestTags.vehicleItem(vehicle.id)).performClick()
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:109:        composeRule.onNodeWithTag(TestTags.recordItem(record.id)).performClick()
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:136:        composeRule.onNodeWithTag(TestTags.vehicleItem(vehicle.id)).performClick()
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:137:        composeRule.onNodeWithTag(TestTags.recordItem(record.id)).performClick()
app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt:150:        composeRule.onNodeWithTag(TestTags.vehicleItem(vehicle.id)).performClick()
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenTest.kt:8:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenTest.kt:44:        composeTestRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/ImageViewerGestureRegressionTest.kt:10:import androidx.compose.ui.test.performClick
app/src/androidTest/java/com/yorvana/regression/attachments/ImageViewerGestureRegressionTest.kt:103:        composeRule.onNodeWithTag(TestTags.vehicleItem(vehicle.id)).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/ImageViewerGestureRegressionTest.kt:105:        composeRule.onNodeWithTag(TestTags.recordItem(record.id)).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/ImageViewerGestureRegressionTest.kt:107:        composeRule.onNodeWithTag(TestTags.recordDetailAttachmentThumbnail("fixture.png")).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:11:import androidx.compose.ui.test.performClick
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:12:import androidx.compose.ui.test.performScrollTo
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:13:import androidx.compose.ui.test.performTextInput
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:84:        composeRule.onNodeWithTag(TestTags.EDIT_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:87:        composeRule.onNodeWithTag(TestTags.ATTACHMENT_PICK_FILE_BUTTON).performScrollTo().performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:122:                composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:153:            composeRule.onNodeWithTag(TestTags.recordDetailAttachmentThumbnail(fixture.displayName)).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:163:        composeRule.onNodeWithTag(TestTags.ATTACHMENT_TAKE_PHOTO_BUTTON).performScrollTo().performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:182:        composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).assertIsEnabled().performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:219:            composeRule.onNodeWithTag(TestTags.recordDetailAttachmentThumbnail("test.pdf")).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:232:        composeRule.onNodeWithContentDescription(composeRule.activity.getString(R.string.record_edit)).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:234:        composeRule.onNodeWithTag(TestTags.removeAttachment("delete-me.txt")).performScrollTo().performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:236:        composeRule.onNodeWithTag(TestTags.CONFIRM_DELETE_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:237:        composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:248:        composeRule.onNodeWithTag(TestTags.SETTINGS_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:250:        composeRule.onNodeWithTag(TestTags.SETTINGS_VAULT_CHANGE_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:253:        composeRule.onNodeWithTag(TestTags.SETTINGS_VAULT_MOVE_ACTION).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:259:        composeRule.onNodeWithText(composeRule.activity.getString(R.string.action_ok)).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:265:            composeRule.onNodeWithTag(TestTags.recordDetailAttachmentThumbnail("after-move.pdf")).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:275:        composeRule.onNodeWithTag(TestTags.ATTACHMENT_TAKE_PHOTO_BUTTON).performScrollTo().performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:286:        composeRule.onNodeWithTag(TestTags.ATTACHMENT_TAKE_PHOTO_BUTTON).performScrollTo().performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:304:        composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:327:        composeRule.onNodeWithText(name).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:333:        composeRule.onNodeWithTag(TestTags.ADD_RECORD_FAB).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:338:        composeRule.onNodeWithTag(TestTags.recordItem(record.id)).performClick()
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:344:        performer: String,
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:346:        composeRule.onNodeWithTag(TestTags.RECORD_ODOMETER_FIELD).performTextInput(odometer)
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:347:        composeRule.onNodeWithTag(TestTags.PERFORMER_FIELD).performTextInput(performer)
app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt:7:import androidx.compose.ui.test.performClick
app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt:51:        composeRule.onNodeWithText(composeRule.activity.getString(R.string.setup_choose_folder)).performClick()
app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt:56:        composeRule.onNodeWithText(composeRule.activity.getString(R.string.setup_complete)).performClick()
app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt:104:        composeRule.onNodeWithTag(TestTags.SETTINGS_VAULT_MOVE_ACTION).performClick()
app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt:119:        composeRule.onNodeWithText(composeRule.activity.getString(R.string.action_ok)).performClick()
app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt:138:        composeRule.onNodeWithTag(TestTags.SETTINGS_VAULT_FRESH_ACTION).performClick()
app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt:152:        composeRule.onNodeWithText(composeRule.activity.getString(R.string.action_ok)).performClick()
app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt:176:            composeRule.onNodeWithTag(TestTags.SETTINGS_VAULT_MOVE_ACTION).performClick()
app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt:204:        composeRule.onNodeWithTag(TestTags.SETTINGS_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/…4399 tokens truncated…()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:207:        composeTestRule.onNodeWithTag(TestTags.VEHICLE_NICKNAME_FIELD).performTextReplacement("New Name")
app/src/androidTest/java/com/yorvana/SmokeTest.kt:208:        composeTestRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:221:        composeTestRule.onNodeWithText("To Delete").performScrollTo().performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:222:        composeTestRule.onNodeWithTag(TestTags.MORE_OPTIONS_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:223:        composeTestRule.onNodeWithText(composeTestRule.activity.getString(R.string.vehicle_delete)).performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:224:        composeTestRule.onNodeWithTag(TestTags.CONFIRM_DELETE_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:237:        composeTestRule.onNodeWithText("Nav Car").performScrollTo().performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:266:        composeTestRule.onNodeWithText(activity.getString(R.string.paywall_sheet_dismiss)).performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:287:        composeTestRule.onNodeWithText("Car A").performTouchInput { swipeLeft() }
app/src/androidTest/java/com/yorvana/SmokeTest.kt:291:        composeTestRule.onNodeWithText("Car A").performScrollTo().performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:303:        composeTestRule.onNodeWithText(activity.getString(R.string.settings_premium_title)).performScrollTo().assertIsDisplayed()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:331:        composeTestRule.onNodeWithText("Car A").performScrollTo().performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:350:            .performScrollTo()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:351:            .performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:360:            .performScrollTo()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:366:            .performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:375:            .performScrollTo()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:388:            .performScrollTo()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:389:            .performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:399:            .performScrollTo()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:400:            .performClick()
app/src/androidTest/java/com/yorvana/SmokeTest.kt:407:        composeTestRule.onNodeWithText(appContext.getString(R.string.action_dismiss)).performClick()
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenIT.kt:7:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenIT.kt:8:import androidx.compose.ui.test.performTextInput
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenIT.kt:91:        composeTestRule.onNodeWithText("Nickname", substring = true, ignoreCase = true).performTextInput("My Car")
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenIT.kt:95:        composeTestRule.onNodeWithTag("MakeField").performClick()
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenIT.kt:96:        composeTestRule.onNodeWithText("Acura").performClick()
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenIT.kt:100:        composeTestRule.onNodeWithText("Model", substring = true, ignoreCase = true).performTextInput("Corolla")
app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenIT.kt:104:        composeTestRule.onNodeWithTag("SaveButton").performClick()
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt:7:import androidx.compose.ui.test.performClick
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt:8:import androidx.compose.ui.test.performScrollTo
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt:41:        composeTestRule.onNodeWithTag(TestTags.ADD_RECORD_FAB).performClick()
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt:52:            .performScrollTo()
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt:53:            .performClick()
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt:66:            .performScrollTo()
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt:75:        composeTestRule.onNodeWithTag(TestTags.ADD_VEHICLE_FAB).performClick()
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt:86:            .performScrollTo()
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt:87:            .performClick()
app/src/androidTest/java/com/yorvana/screenshots/StoreScreenshotTest.kt:93:        composeTestRule.onNodeWithTag(TestTags.SETTINGS_BUTTON).performClick()
app/src/androidTest/java/com/yorvana/testsupport/gestures/Gestures.kt:7:import androidx.compose.ui.test.performTouchInput
app/src/androidTest/java/com/yorvana/testsupport/gestures/Gestures.kt:21:        performTouchInput {
app/src/androidTest/java/com/yorvana/testsupport/gestures/Gestures.kt:36:        performTouchInput {
app/src/androidTest/java/com/yorvana/testsupport/gestures/Gestures.kt:42:        performTouchInput {
app/src/test/java/com/yorvana/ui/settings/SettingsScreenshotTest.kt:8:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/settings/SettingsScreenshotTest.kt:9:import androidx.compose.ui.test.performScrollTo
app/src/test/java/com/yorvana/ui/settings/SettingsScreenshotTest.kt:113:        composeTestRule.onNodeWithTag(TestTags.PRIVACY_LEARN_MORE).performClick()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenshotTest.kt:124:            .performScrollTo()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenshotTest.kt:135:            .performScrollTo()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenshotTest.kt:146:            .performScrollTo()
app/src/androidTest/java/com/yorvana/testsupport/RegressionHelpers.kt:7:import androidx.compose.ui.test.performClick
app/src/androidTest/java/com/yorvana/testsupport/RegressionHelpers.kt:40:            onNodeWithText("Dismiss").performClick()
app/src/androidTest/java/com/yorvana/testsupport/RegressionHelpers.kt:107:    performer: String? = "Self",
app/src/androidTest/java/com/yorvana/testsupport/RegressionHelpers.kt:119:            performer = performer,
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenTest.kt:8:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenTest.kt:42:        composeTestRule.onNodeWithTag("AddCategoryButton").performClick()
app/src/androidTest/java/com/yorvana/testsupport/VaultFixture.kt:53:                performer = "Self",
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:7:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:8:import androidx.compose.ui.test.performScrollTo
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:57:        composeTestRule.onNodeWithText("odometer unit", substring = true, ignoreCase = true).performScrollTo().assertIsDisplayed()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:58:        composeTestRule.onNodeWithText("km", substring = true, ignoreCase = true).performScrollTo().assertIsDisplayed()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:60:        composeTestRule.onNodeWithText("Currency", substring = true, ignoreCase = true).performScrollTo().assertIsDisplayed()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:61:        composeTestRule.onNodeWithText("EUR", substring = true, ignoreCase = true).performScrollTo().assertIsDisplayed()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:64:        composeTestRule.onNodeWithText("Version", substring = true, ignoreCase = true).performScrollTo().assertIsDisplayed()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:65:        composeTestRule.onNodeWithText("1.0.0", substring = true, ignoreCase = true).performScrollTo().assertIsDisplayed()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:92:        composeTestRule.onNodeWithText("categories", substring = true, ignoreCase = true).performScrollTo().performClick()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:121:        composeTestRule.onNodeWithTag(TestTags.SETTINGS_CONTACT_ROW).performScrollTo().performClick()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:152:        composeTestRule.onNodeWithText("OK").performClick()
app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:194:        composeTestRule.onNodeWithText("Use this vault").performClick()
app/src/test/java/com/yorvana/MainActivityTest.kt:5:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/MainActivityTest.kt:86:        composeTestRule.onNodeWithText("Dismiss").performClick()
app/src/test/java/com/yorvana/data/repository/WorkflowIntegrationIT.kt:61:            val catId = createCustomCategory("Performance Tuning")
app/src/test/java/com/yorvana/data/repository/WorkflowIntegrationIT.kt:74:            verifyRecordAndAttachment("v1", "r1", "Performance Tuning", "receipt.jpg")
app/src/test/java/com/yorvana/ui/components/UpgradeDialogTest.kt:5:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/components/UpgradeDialogTest.kt:34:        composeTestRule.onNodeWithText("Upgrade Now", ignoreCase = true).performClick()
app/src/test/java/com/yorvana/ui/vehicles/VehicleListScreenIT.kt:8:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/vehicles/VehicleListScreenIT.kt:9:import androidx.compose.ui.test.performTouchInput
app/src/test/java/com/yorvana/ui/vehicles/VehicleListScreenIT.kt:126:        composeTestRule.onNodeWithTag("AddVehicleFab").performClick()
app/src/test/java/com/yorvana/ui/vehicles/VehicleListScreenIT.kt:163:        composeTestRule.onNodeWithText("Test Car").performClick()
app/src/test/java/com/yorvana/ui/vehicles/VehicleListScreenIT.kt:201:        composeTestRule.onNodeWithText("Test Car").performTouchInput {
app/src/test/java/com/yorvana/ui/vehicles/VehicleListConsentDialogTest.kt:9:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/vehicles/VehicleListConsentDialogTest.kt:81:        composeTestRule.onNodeWithText(getString(R.string.consent_dialog_enable)).performClick()
app/src/test/java/com/yorvana/ui/vehicles/VehicleListConsentDialogTest.kt:93:        composeTestRule.onNodeWithText(getString(R.string.consent_dialog_not_now)).performClick()
app/src/test/java/com/yorvana/ui/vehicles/VehicleListConsentDialogTest.kt:112:        composeTestRule.onNodeWithTag(TestTags.PRIVACY_LEARN_MORE).performClick()
app/src/test/java/com/yorvana/ui/vehicles/VehicleListConsentDialogTest.kt:121:        composeTestRule.onNodeWithTag(TestTags.PRIVACY_LEARN_MORE).performClick()
app/src/test/java/com/yorvana/ui/vehicles/VehicleListConsentDialogTest.kt:122:        composeTestRule.onNodeWithText(getString(R.string.action_show_less)).performClick()
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenshotTest.kt:6:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenshotTest.kt:74:        composeTestRule.onNodeWithTag("AddCategoryButton").performClick()
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenIT.kt:7:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenIT.kt:8:import androidx.compose.ui.test.performTextInput
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenIT.kt:71:        composeTestRule.onNodeWithTag("AddCategoryButton").performClick()
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenIT.kt:74:        composeTestRule.onNodeWithText("Category name", substring = true, ignoreCase = true).performTextInput("New Cat")
app/src/test/java/com/yorvana/ui/settings/CategoriesScreenIT.kt:81:        composeTestRule.onNodeWithTag("SaveButton").performClick()
app/src/test/java/com/yorvana/ui/reportbug/ReportBugScreenshotTest.kt:7:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/reportbug/ReportBugScreenshotTest.kt:8:import androidx.compose.ui.test.performScrollTo
app/src/test/java/com/yorvana/ui/reportbug/ReportBugScreenshotTest.kt:74:        composeTestRule.onNodeWithTag(TestTags.REPORT_BUG_DEVICE_INFO_CARD).performScrollTo().performClick()
app/src/test/java/com/yorvana/ui/reportbug/ReportBugScreenTest.kt:8:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/reportbug/ReportBugScreenTest.kt:9:import androidx.compose.ui.test.performScrollTo
app/src/test/java/com/yorvana/ui/reportbug/ReportBugScreenTest.kt:91:        composeTestRule.onNodeWithTag(TestTags.REPORT_BUG_DEVICE_INFO_CARD).performScrollTo().performClick()
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:148:            // Initial state: date is pre-populated, but odometer and performer are empty
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:152:            // Still false because performer is missing
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:227:    fun `onEvent IsDiyChanged should clear performer and not unreachable sentinel`() =
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:233:            assertThat(viewModel.state.value.performer).isEmpty()
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:237:            assertThat(viewModel.state.value.performer).isEmpty() // Should still be empty as it was cleared
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:245:            assertThat(viewModel.state.value.performer).isEqualTo("Mechanic")
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:248:            assertThat(viewModel.state.value.performer).isEqualTo("Mechanic") // Should not update
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:261:    fun `save should fail if performer is blank and not DIY`() =
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:270:            assertThat(viewModel.state.value.performerError).isEqualTo("required")
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:274:    fun `save should succeed if DIY is true even if performer text is blank`() =
app/src/test/java/com/yorvana/ui/records/AddEditRecordViewModelTest.kt:284:            coVerify { recordRepo.saveRecord(match { it.performer == PERFORMER_DIY }) }
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenIT.kt:7:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenIT.kt:116:        composeTestRule.onNodeWithTag("AddRecordFab").performClick()
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenIT.kt:155:        composeTestRule.onNodeWithText("Oil Change", substring = true, ignoreCase = true).performClick()
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenIT.kt:170:                performer = PERFORMER_DIY,
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenIT.kt:194:        // Verify "Myself" is displayed for the DIY performer
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenIT.kt:198:                .getString(com.yorvana.R.string.record_performer_self)
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenTest.kt:8:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenTest.kt:44:        composeTestRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenTest.kt:135:                performerError = "E",
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenTest.kt:181:        stateFlow.value = stateFlow.value.copy(performer = "Mechanic", isDiy = false)
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenTest.kt:202:        val performerLabel = composeTestRule.activity.getString(com.yorvana.R.string.record_performer)
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenTest.kt:204:        val requiredLabels = listOf(dateLabel, odometerLabel, performerLabel)
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenTest.kt:222:                performer = "Mechanic",
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:8:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:9:import androidx.compose.ui.test.performTextInput
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:70:        composeTestRule.onNodeWithText("Odometer", substring = true, ignoreCase = true).performTextInput("1234")
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:74:        composeTestRule.onNodeWithText("Cost", substring = true, ignoreCase = true).performTextInput("50.0")
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:78:        composeTestRule.onNodeWithText("Notes", substring = true, ignoreCase = true).performTextInput("Some notes")
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:82:        composeTestRule.onNodeWithTag("PerformerField").performTextInput("Mechanic Joe")
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:86:        composeTestRule.onNodeWithText("DIY", substring = true, ignoreCase = true).performClick()
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:90:        composeTestRule.onNodeWithTag("SaveButton").performClick()
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:95:    fun diy_toggle_disables_performer_field() {
app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:112:        // The performer field should be disabled when DIY is checked
app/src/test/java/com/yorvana/ui/records/RecordDetailScreenIT.kt:35:                performer = "Mechanic Joe",
app/src/test/java/com/yorvana/ui/records/RecordDetailScreenIT.kt:60:        // Verify category and performer are displayed
app/src/test/java/com/yorvana/ui/records/RecordDetailScreenIT.kt:75:                performer = PERFORMER_DIY,
app/src/test/java/com/yorvana/ui/records/RecordDetailScreenIT.kt:100:        // Verify "Myself" is displayed for the DIY performer
app/src/test/java/com/yorvana/ui/records/RecordDetailScreenIT.kt:104:                .getString(com.yorvana.R.string.record_performer_self)
app/src/test/java/com/yorvana/ui/navigation/SetupScreenshotTest.kt:6:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/navigation/SetupScreenshotTest.kt:67:        composeTestRule.onNodeWithTag(TestTags.PRIVACY_LEARN_MORE).performClick()
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenshotTest.kt:6:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenshotTest.kt:84:        composeTestRule.onNodeWithTag("MoreOptionsButton").performClick()
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenshotTest.kt:95:        composeTestRule.onNodeWithTag("MoreOptionsButton").performClick()
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenTest.kt:8:import androidx.compose.ui.test.performClick
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenTest.kt:55:        composeTestRule.onNodeWithContentDescription(moreLabel).performClick()
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenTest.kt:59:        composeTestRule.onNodeWithText(editLabel).performClick()
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenTest.kt:153:        composeTestRule.onNodeWithText("Add Record", useUnmergedTree = true).performClick()
app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenTest.kt:192:        composeTestRule.onNodeWithText("Oil Change").performClick()