Output:
Chunk ID: 1baeea
Wall time: 0.3529 seconds
Process exited with code 0
Original token count: 2691
Output:
# GMD Smoke Test Scenarios (Phase 3)

## Purpose

This document defines the detailed scenarios for the E2E smoke test suite that will run on Gradle Managed Devices (GMD). These tests verify critical happy-path user journeys on a real Android OS, complementing the comprehensive Robolectric-based integration tests that cover feature-level correctness.

Smoke tests are **not** intended to replace integration tests. They exist to catch issues that only surface on a real Android environment: actual Activity lifecycle, real filesystem access via SAF, real Compose rendering pipeline, and cross-screen navigation with real back-stack behavior.

## Scope & Constraints

- **Runner:** `AndroidJUnit4` with `createAndroidComposeRule<MainActivity>()`
- **Device:** Gradle Managed Device — Pixel 2, API 33 (aosp)
- **Execution time budget:** The entire smoke suite should complete in under 2 minutes on a single GMD instance
- **No external dependencies:** Tests must not require network, Google Play Billing, or pre-existing device state
- **Test file:** `app/src/androidTest/java/com/yorvana/SmokeTest.kt` — single canonical entry point (replaces the instrumented `app/src/androidTest/java/com/yorvana/MainActivityTest.kt`)
- **Coverage baseline:** Output is captured as `app/coverage-baselines/gmd_smoke.ec` via the `generateGmdCoverage` Gradle task

## Test Design Principles

1. **Two state strategies** — the core happy path (S01–S06) runs as a single multi-step flow test that accumulates state within one Activity launch; all other scenarios (S07–S17) are independent tests that reset DataStore and vault state in `@Before` and use setup helpers to reach the required precondition. See "Shared State vs. Independent Tests" in Implementation Notes for rationale.
2. **Semantic selectors** — use `testTag`, `contentDescription`, and visible text; avoid index-based selectors
3. **Wait-for-idle** — use `waitUntil` / `waitForIdle` instead of fixed delays
4. **Minimal assertions** — verify the screen rendered and key elements are present; leave exhaustive validation to Robolectric tests

---

## Scenarios

### S01: First Launch — Setup Screen

**Precondition:** Fresh install, no vault configured.

**Flow:**
1. App launches to `SetupScreen`
2. Verify "Welcome" title text is displayed
3. Verify setup description text is displayed
4. Verify "Choose Folder" button is visible and enabled

**Verifies:** App cold-starts successfully, correct entry point for first-time users, `MainActivity` resolves the start destination to `Setup`.

---

### S02: Vault Configuration & Sentry Opt-in

**Precondition:** App is on `SetupScreen`.

**Flow:**
1. Tap "Choose Folder" button — SAF folder picker launches
2. Select or create a folder in the picker
3. App moves to Setup Step 2 (Sentry opt-in)
4. Verify "Help improve the app" title and toggle are visible
5. Toggle the switch and tap "Complete Setup"
6. App navigates to `VehicleListScreen` ("My Garage")
7. Verify the "My Garage" title is displayed

**Verifies:** SAF integration works, 2-step setup flow is functional, vault URI and Sentry preference are persisted, `YorvanaApplication.applyVaultUri()` initializes storage correctly.

**Note:** Interacting with the system SAF picker requires UiAutomator (`UiDevice`). If SAF interaction proves flaky or overly complex, this scenario can be simplified to only verify the picker launches (intent is fired) and the remaining flow can be tested by pre-configuring the vault in a `@Before` method using test fixtures.

---

### S03: Add a Vehicle

**Precondition:** Vault is configured, `VehicleListScreen` is displayed with empty state.

**Flow:**
1. Tap the FAB / "Add Vehicle" button
2. `AddEditVehicleScreen` is displayed
3. Verify the "Add Vehicle" title is shown
4. Enter a nickname (e.g., "Test Car") in the Nickname field
5. Optionally fill Make (e.g., "Toyota") and Year (e.g., "2020")
6. Tap the "Save" button
7. App navigates back to `VehicleListScreen`
8. Verify "Test Car" appears in the vehicle list
9. Verify the empty state is no longer shown

**Verifies:** Navigation to AddEditVehicle, form input works, vehicle is persisted to the vault filesystem, vehicle list updates on return, denormalized stats are initialized.

---

### S04: View Vehicle Detail

**Precondition:** At least one vehicle ("Test Car") exists in the list.

**Flow:**
1. Tap on "Test Car" in the vehicle list
2. `VehicleDetailScreen` is displayed
3. Verify "Test Car" appears as the vehicle name/title
4. Verify the empty records state ("No service records yet…") is shown
5. Verify the "Add Record" FAB / button is visible

**Verifies:** Navigation from VehicleList to VehicleDetail with correct vehicle ID, vehicle data loads from storage, empty records state renders.

---

### S05: Add a Service Record

**Precondition:** On `VehicleDetailScreen` for "Test Car" with no records.

**Flow:**
1. Tap the "Add Record" FAB / button
2. `AddEditRecordScreen` is displayed
3. Verify the "Add Record" title is shown
4. Verify the date field is non-empty and contains a valid, parseable date (do not assert an exact "today" string — it can be flaky around midnight, timezone, or locale changes; instead compare against the same formatter the app uses, or simply assert the field is populated)
5. Enter an odometer reading (e.g., "50000")
6. Select a service category from the dropdown (e.g., "Oil Change")
7. Enter a performer (e.g., "Myself")
8. Optionally enter a cost (e.g., "49.99")
9. Tap the "Save" button
10. App navigates back to `VehicleDetailScreen`
11. Verify the new record appears in the records list (showing "Oil Change" and/or the date)
12. Verify the empty records state is no longer shown

**Verifies:** Navigation to AddEditRecord, form fields render with defaults, category selector works, record is persisted to vault filesystem, vehicle detail updates on return.

---

### S06: View Record Detail

**Precondition:** On `VehicleDetailScreen` with at least one record.

**Flow:**
1. Tap on the service record in the list
2. `RecordDetailScreen` is displayed
3. Verify the record date is shown
4. Verify the odometer reading is displayed — assert on stable pieces (e.g., the raw number via substring/regex and the unit separately) rather than an exact formatted string like "50,000 km", since formatting is locale-dependent (thousands separator, spacing). Set the locale and unit deterministically in test setup if an exact match is needed.
5. Verify the category ("Oil Change") is displayed
6. Verify the performer ("Myself") is displayed
7. Verify the cost is displayed (if entered)

**Verifies:** Navigation from VehicleDetail to RecordDetail, record data loads correctly from storage, all fields render.

---

### S07: Add an Attachment via File Picker

**Precondition:** On `AddEditRecordScreen` (either creating a new record or editing an existing one).

**Flow:**
1. Scroll to the "Attachments" section
2. Verify the "Choose file" button is visible
3. Verify the "Take photo" button is visible
4. Tap the "Choose file" button — system file picker launches
5. Select a file (e.g., a test image or PDF pre-pushed to the device)
6. Verify the attachment thumbnail/entry appears in the Attachments section with the filename
7. Tap "Save"
8. App navigates back to the previous screen

**Verifies:** File picker intent launches correctly, picked file URI is resolved and copied into the vault, attachment metadata is persisted with the record.

**Note:** Selecting a file in the system picker requires UiAutomator (`UiDevice`). A test fixture file should be pre-pushed to the emulator's Downloads folder via an `adb push` step in the test setup or Gradle task. If picker interaction proves flaky, verify the intent fires and test the rest of the attachment flow by injecting a URI directly via the ViewModel event.

---

### S08: View Attachment on Record Detail

**Precondition:** A record with at least one attachment exists (created in S07 or set up via test fixture).

**Flow:**
1. Navigate to `RecordDetailScreen` for the record with attachments
2. Verify the "Attachments" section header is displayed with the correct count (e.g., "Attachments (1)")
3. Verify the attachment card is displayed with the filename
4. For image attachments: verify the thumbnail renders (non-null image content)
5. Tap on an image attachment
6. Verify `ImageViewerScreen` is displayed
7. Press back to return to `RecordDetailScreen`

**Verifies:** Attachments are loaded from vault storage, thumbnails render from content URIs, navigation to ImageViewer works with correct URI parameter, back navigation from ImageViewer returns to RecordDetail.

---

### S09: Remove an Attachment

**Precondition:** On `AddEditRecordScreen` in edit mode for a record that has at least one attachment.

**Flow:**
1. Scroll to the "Attachments" section
2. Verify the existing attachment is displayed
3. Tap the "Remove" button on the attachment
4. Verify a confirmation dialog appears (naming the attachment file)
5. Tap "Delete" / "Confirm" in the dialog
6. Verify the attachment is no longer displayed in the Attachments section
7. Tap "Save"
8. Navigate to `RecordDetailScreen` for the same record
9. Verify the Attachments section is empty or absent

**Verifies:** Attachment removal confirmation guard works, attachment is removed from the UI state, deletion is persisted to vault storage on save, attachment file is cleaned up from the filesystem.

---

### S10: Take a Photo Attachment

**Precondition:** On `AddEditRecordScreen` (creating or editing a record), device has a camera available.

**Flow:**
1. Scroll to the "Attachments" section
2. Tap the "Take photo" button — camera app launches
3. Capture a photo (or simulate capture via test intent)
4. Verify the photo appears as a thumbnail in the Attachments section
5. Tap "Save"

**Verifies:** Camera intent launches correctly with the pre-generated output URI (`FileProviderHelper`), captured photo is resolved and stored in the vault, attachment metadata is persisted.

**Note:** Camera simulation on GMD emulators can be configured via `hw.camera.back = emulated` in the AVD config. If camera interaction is unreliable, verify the intent fires and test attachment persistence via file picker (S07) instead.

---

### S11: Navigate to Settings

**Precondition:** App is on any screen with access to Settings (typically VehicleListScreen via a menu or top-bar icon).

**Flow:**
1. Navigate back to `VehicleListScreen` (if not already there)
2. Tap the Settings icon / menu entry
3. `SettingsScreen` is displayed
4. Verify "Settings" title is shown
5. Verify the odometer unit setting is visible (km/miles)
6. Verify the currency setting is visible
7. Verify the data folder / vault location is displayed


sed -n '1,220p' gradle/libs.versions.toml


Output:
Chunk ID: 5f72f0
Wall time: 0.3043 seconds
Process exited with code 0
Original token count: 63
Output:
.github/dependabot.yml
.github/workflows/deploy-play-store.yml
.github/workflows/ci.yml
.github/workflows/smoke.yml
.github/workflows/semgrep.yml
.github/workflows/ci-label.yml
.github/workflows/smoke-label.yml
.github/workflows/baseline-reminder.yml


Output:
Chunk ID: 21e52d
Wall time: 0.2695 seconds
Process exited with code 0
Original token count: 1377
Output:
            "src/main/java",
            "src/main/kotlin",
            "src/debug/java",
            "src/debug/kotlin",
        ),
    )

    // Collect all execution data from both unit tests and instrumented tests
    // Using specific roots to avoid implicit dependency issues with other tasks' outputs in the build folder
    val executionDataFiles =
        files(
            fileTree(layout.buildDirectory.dir("outputs/unit_test_code_coverage")) { include("**/*.exec") },
            layout.projectDirectory.file("coverage-baselines/gmd_smoke.ec"),
            fileTree(layout.buildDirectory.dir("jacoco")) { include("**/*.exec") },
        )

    executionData.setFrom(executionDataFiles)

    doFirst {
        val lfsBaseline = layout.projectDirectory.file("coverage-baselines/gmd_smoke.ec").asFile
        if (!lfsBaseline.exists()) {
            throw GradleException(
                "Required coverage baseline is missing: ${lfsBaseline.path}. " +
                    "Run 'git lfs pull' to fetch tracked baselines or './gradlew generateGmdCoverage' to regenerate it.",
            )
        }
        if (lfsBaseline.length() < 1000) {
            val header = lfsBaseline.inputStream().use { it.readNBytes(100) }.toString(Charsets.UTF_8)
            if (header.startsWith("version https://git-lfs.github.com/spec/v1")) {
                throw GradleException("Coverage baseline is a Git LFS pointer. Please run 'git lfs pull' to fetch the actual file.")
            }
        }

        val existingFiles = executionDataFiles.files.filter { it.exists() }
        val dynamicExecutionFiles = existingFiles.filter { it.name != "gmd_smoke.ec" }
        if (dynamicExecutionFiles.isEmpty()) {
            throw GradleException(
                "No dynamic JaCoCo execution data files were found. " +
                    "Ensure tests were executed with coverage enabled (e.g., via -Pcoverage).\n" +
                    "Searched in:\n" +
                    "- ${layout.buildDirectory.dir("outputs/unit_test_code_coverage").get()}\n" +
                    "- ${layout.buildDirectory.dir("jacoco").get()}",
            )
        }
    }
}

tasks.withType<Test>().configureEach {
    if (isCoverageEnabled) {
        configure<JacocoTaskExtension> {
            isEnabled = true
            // Required for Robolectric
            isIncludeNoLocationClasses = true
            excludes = listOf("jdk.internal.*")
        }
    } else {
        configure<JacocoTaskExtension> { isEnabled = false }
    }
    if (name.contains("Release")) {
        enabled = false
    }

    val defaultForks = (Runtime.getRuntime().availableProcessors() / 4).coerceAtLeast(2)
    maxParallelForks = (project.findProperty("testForks") as String?)?.toInt() ?: defaultForks
    forkEvery = 0L

    minHeapSize = "512m"
    maxHeapSize = "1536m"
    jvmArgs =
        listOf(
            "-XX:+UseG1GC",
            "-XX:MaxMetaspaceSize=512m",
            "-Dfile.encoding=UTF-8",
        )

    systemProperty("robolectric.graphicsMode", "NATIVE")
    // Exclude tests tagged @Category(LocalOnly::class) from automated Gradle runs.
    // These tests are intended to be run directly from the IDE.
    // Note: This project uses JUnit 4 exclusively. If a Jupiter runner is ever added,
    // useJUnit { } will need to be scoped to only JUnit 4 tasks to avoid overriding
    // Jupiter configuration.
    useJUnit {
        excludeCategories("com.yorvana.LocalOnly")
    }

    reports {
        html.required.set(System.getenv("CI") != null)
        junitXml.required.set(true)
    }
}

dependencies {
    implementation(libs.androidx.core.ktx)
    implementation(libs.androidx.activity.compose)
    implementation(libs.androidx.documentfile)
    implementation(libs.sentry.android)
    implementation(platform(libs.compose.bom))
    implementation(libs.compose.ui)
    implementation(libs.compose.ui.tooling.preview)
    implementation(libs.compose.material3)
    implementation(libs.compose.material.icons.extended)
    implementation(libs.navigation3.runtime)
    implementation(libs.navigation3.ui)
    implementation(libs.lifecycle.viewmodel.compose)
    implementation(libs.lifecycle.viewmodel.savedstate)
    implementation(libs.lifecycle.runtime.compose)
    implementation(libs.datastore.preferences)
    implementation(libs.kotlinx.serialization.json)
    implementation(libs.kotlinx.coroutines.android)
    implementation(libs.coil.compose)
    implementation(libs.google.billing)

    debugImplementation(libs.compose.ui.tooling)
    debugImplementation(libs.compose.ui.test.manifest)

    testImplementation(libs.junit)
    testImplementation(libs.kotlinx.coroutines.test)
    testImplementation(libs.mockk)
    testImplementation(libs.truth)
    testImplementation(libs.turbine)
    testImplementation(libs.robolectric)
    testImplementation(libs.compose.ui.test)
    testImplementation(libs.androidx.test.ext)
    testImplementation(libs.roborazzi)
    testImplementation(libs.roborazzi.compose)

    androidTestImplementation(platform(libs.compose.bom))
    androidTestImplementation(libs.androidx.test.ext)
    androidTestImplementation(libs.compose.ui.test)
    androidTestImplementation(libs.mockk.android)
    androidTestImplementation(libs.truth)
    androidTestImplementation(libs.turbine)
    androidTestImplementation(libs.roborazzi)
    androidTestImplementation(libs.roborazzi.compose)
    androidTestImplementation(libs.uiautomator)
    androidTestImplementation(libs.espresso.intents)
}


Output:
Chunk ID: 15d24f
Wall time: 0.3156 seconds
Process exited with code 0
Original token count: 1111
Output:
[versions]
ktlint = "12.1.1"
detekt = "1.23.6"
git-hooks = "0.0.2"
mockk = "1.14.9"
truth = "1.4.5"
turbine = "1.2.1"
agp = "8.9.1"
kotlin = "2.3.21"
paparazzi = "1.3.5"
roborazzi = "1.11.0"
compose-bom = "2026.04.01"
navigation3 = "1.1.1"
lifecycle = "2.10.0"
datastore = "1.1.1"
kotlinx-serialization = "1.11.0"
kotlinx-coroutines = "1.10.2"
coil = "2.6.0"
documentfile = "1.1.0"
core-ktx = "1.18.0"
activity-compose = "1.13.0"
robolectric = "4.16.1"
billing = "8.3.0"
sentry = "8.41.0"

[libraries]
sentry-android = { module = "io.sentry:sentry-android", version.ref = "sentry" }
robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" }
truth = { module = "com.google.truth:truth", version.ref = "truth" }
turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "core-ktx" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activity-compose" }
androidx-documentfile = { module = "androidx.documentfile:documentfile", version.ref = "documentfile" }
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "compose-bom" }
compose-ui = { module = "androidx.compose.ui:ui" }
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
compose-material3 = { module = "androidx.compose.material3:material3" }
compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" }
navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "navigation3" }
navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "navigation3" }
lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" }
lifecycle-viewmodel-savedstate = { module = "androidx.lifecycle:lifecycle-viewmodel-savedstate", version.ref = "lifecycle" }
lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" }
datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" }
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
junit = { module = "junit:junit", version = "4.13.2" }
kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" }
androidx-test-ext = { module = "androidx.test.ext:junit", version = "1.3.0" }
compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4" }
compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" }
roborazzi = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = "roborazzi" }
roborazzi-compose = { module = "io.github.takahirom.roborazzi:roborazzi-compose", version.ref = "roborazzi" }
roborazzi-junit = { module = "io.github.takahirom.roborazzi:roborazzi-junit", version.ref = "roborazzi" }
uiautomator = { module = "androidx.test.uiautomator:uiautomator", version = "2.3.0" }
espresso-intents = { module = "androidx.test.espresso:espresso-intents", version = "3.7.0" }
google-billing = { module = "com.android.billingclient:billing-ktx", version.ref = "billing" }

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
paparazzi = { id = "app.cash.paparazzi", version.ref = "paparazzi" }
roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
git-hooks = { id = "com.github.jakemarsden.git-hooks", version.ref = "git-hooks" }


Output:
Chunk ID: b35207
Wall time: 0.3274 seconds
Process exited with code 0
Original token count: 166
Output:
# On Fedora 43+ (kernel ≥ 6.17) the JVM prints cgroup warnings to stdout, which
# corrupts the `jlink --version` output that AGP's JdkImageTransform parses for the
# --module-version argument, causing the transform to fail.  Set this env var before
# running Gradle to suppress the noise:
#   export JAVA_TOOL_OPTIONS="-Xlog:os+container=off"
org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.caching=true
android.useAndroidX=true
android.enableJetifier=false
android.suppressUnsupportedCompileSdk=35
kotlin.code.style=official
robolectric.includeAndroidResources=true

android.testoptions.manageddevices.emulator.gpu=host


The repo already documents the validation stack: JVM checks through Gradle, smoke tests through a Pixel 2 API 33 managed device, and GPU mode defaults to `host` locally. I’m checking CI workflows now so the merge gate matches what GitHub will enforce.


sed -n '1,260p' .github/workflows/ci.yml


sed -n '1,260p' .github/workflows/smoke.yml


Output:
Chunk ID: 756973
Wall time: 0.2494 seconds
Process exited with code 0
Original token count: 1022
Output:
name: CI
permissions:
  contents: read
  checks: write

env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

on:
  workflow_dispatch:
  workflow_call:

concurrency:
  group: ci-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint-test:
    name: Lint, Static Analysis & Tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          lfs: true

      - name: Set up JDK 17
        uses: actions/setup-java@v5
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Set up Gradle
        uses: gradle/actions/setup-gradle@v6
        with:
          cache-read-only: ${{ github.ref != 'refs/heads/master' }}
          cache-cleanup: on-success

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Run Lint, Static Analysis & JVM Tests with Coverage
        run: ./gradlew lintDebug ktlintCheck detekt verifyWithCoverage -Proborazzi.test.verify=true -Pcoverage

      - name: Upload HTML Coverage Report
        uses: actions/upload-artifact@v7
        with:
          name: jacoco-html-report
          path: app/build/reports/jacoco/verifyWithCoverage/html/
          retention-days: 7

      - name: Upload Roborazzi Results
        if: failure()
        uses: actions/upload-artifact@v7
        with:
          name: roborazzi-results
          path: app/build/test-results/roborazzi
          retention-days: 7

  build:
    name: Build & Verify Release
    runs-on: ubuntu-latest
    needs: [ lint-test ]
    steps:
      - name: Checkout code
        uses: actions/checkout@v6

      - name: Set up JDK 17
        uses: actions/setup-java@v5
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Set up Gradle
        uses: gradle/actions/setup-gradle@v6
        with:
          cache-read-only: ${{ github.ref != 'refs/heads/master' }}
          cache-cleanup: on-success

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Build Debug APK
        run: ./gradlew assembleDebug

      - name: Build Release APK & Minify with R8
        run: ./gradlew assembleRelease

      - name: Verify R8 Stripping (DebugBillingOverride)
        run: |
          MAPPING_FILE="app/build/outputs/mapping/release/mapping.txt"
          if [ ! -f "$MAPPING_FILE" ]; then
            echo "Error: R8 mapping file not found at $MAPPING_FILE"
            exit 1
          fi
          # Match only top-level class entries (e.g. "com.yorvana.data.billing.DebugBillingOverride -> x:"
          # or nested "...DebugBillingOverride$Inner -> ..."), not method-signature references
          # in other classes that R8 retains for stack-trace deobfuscation, and not the
          # DebugBillingOverrideMode enum (which is intentionally retained in release).
          if grep -qE '^com\.yorvana\.data\.billing\.DebugBillingOverride( |\$)' "$MAPPING_FILE"; then
            echo "Error: DebugBillingOverride class found in release mapping file. R8 stripping failed!"
            grep -E '^com\.yorvana\.data\.billing\.DebugBillingOverride( |\$)' "$MAPPING_FILE"
            exit 1
          fi
          echo "Success: DebugBillingOverride class is absent from release build."

      - name: Upload Debug APK
        uses: actions/upload-artifact@v7
        with:
          name: debug-apk
          path: app/build/outputs/apk/debug/*.apk
          if-no-files-found: error
          retention-days: 1

  ci-success:
    name: CI Success
    if: ${{ always() }}
    runs-on: ubuntu-latest
    needs: [ lint-test, build ]
    steps:
      - name: Verify required jobs succeeded
        run: |
          if [ "${{ needs.lint-test.result }}" != "success" ] || \
             [ "${{ needs.build.result }}" != "success" ]; then
            echo "One or more required jobs did not succeed:"
            echo "lint-test=${{ needs.lint-test.result }}"
            echo "build=${{ needs.build.result }}"
            exit 1
          fi

          echo "All checks passed successfully!"


Output:
Chunk ID: 0cfdd3
Wall time: 0.2730 seconds
Process exited with code 0
Original token count: 619
Output:
# GMD smoke tests on CI.
#
# GitHub-hosted `ubuntu-latest` runners expose `/dev/kvm` for CPU acceleration
# (the "Enable KVM" step installs a world-writable `0666` udev rule, which is
# a common approach when running GMD tests on ephemeral Ubuntu runners to
# ensure the emulator process has necessary permissions).
# However, they have no GPU/Vulkan driver — so the GPU mode is forced to
# `swiftshader_indirect`. Note: swiftshader is documented to crash on Linux
# kernel >= 6.17 (see TESTING_SETUP.md). GitHub-hosted runners are currently
# on kernel ~6.8, but if that changes this workflow will need a different
# harness.
#
# To keep Free-tier Actions minutes in budget, this workflow does not auto-trigger.
# It runs only when the `smoke` label is added to a PR (via smoke-label.yml) or
# when manually invoked from the Actions tab (workflow_dispatch).
name: Smoke
permissions:
  contents: read

on:
  workflow_dispatch:
  workflow_call:

jobs:
  smoke:
    name: GMD Smoke Tests
    runs-on: ubuntu-latest
    timeout-minutes: 45
    concurrency:
      group: smoke-${{ github.ref }}
      cancel-in-progress: true
    steps:
      - name: Checkout code
        uses: actions/checkout@v6

      - name: Set up JDK 17
        uses: actions/setup-java@v5
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Set up Gradle
        uses: gradle/actions/setup-gradle@v6
        with:
          cache-read-only: ${{ github.ref != 'refs/heads/master' }}
          cache-cleanup: on-success

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Enable KVM
        run: |
          echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
            | sudo tee /etc/udev/rules.d/99-kvm4all.rules
          sudo udevadm control --reload-rules
          sudo udevadm trigger --name-match=kvm

      - name: Run GMD Smoke Tests
        run: ./gradlew pixel2api33Check -Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirect

      - name: Upload Managed Device Reports
        if: failure()
        uses: actions/upload-artifact@v7
        with:
          name: gmd-test-reports
          path: |
            app/build/reports/androidTests/managedDevice/
            app/build/outputs/androidTest-results/managedDevice/
            app/build/outputs/managed_device_android_test_additional_output/
          if-no-files-found: warn
          retention-days: 7


Output:
Chunk ID: 948ab2
Wall time: 0.2695 seconds
Process exited with code 0
Original token count: 52
Output:
version: 2
updates:
  - package-ecosystem: "gradle"
    directory: "/"
    schedule:
      interval: "weekly"
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"


CI uses two main gates: `lintDebug ktlintCheck detekt verifyWithCoverage -Proborazzi.test.verify=true -Pcoverage`, then `assembleDebug` and `assembleRelease`; smoke is a separate managed-device workflow. A local PR validation plan should include those explicitly plus `pixel2api33Check` with host GPU unless you want CI-parity SwiftShader.


Question: Which merge strategy should the procedure use for green Dependabot PRs?

Choices:
Keeps dependency bumps as one clean commit per PR and is common for Dependabot.
Preserves the PR branch history exactly, but adds a merge commit.
Creates linear history from PR commits, but can be awkward after fix commits.

Question: For local validation before merging, which command set should be the required gate?

Choices:
Run CI-equivalent Gradle checks and GMD smoke with `gpu=host` for local GPU acceleration.
Run CI-equivalent checks and smoke with `gpu=swiftshader_indirect`, matching GitHub but not local GPU acceleration.
Run smoke only for PRs touching app/runtime behavior; faster, but less strict for dependency bumps.


Output: {"answers":{"validation_scope":{"answers":["CI plus host smoke (Recommended)"]},"merge_strategy":{"answers":["Squash merge (Recommended)"]}}}


I’ll also confirm the exact changed files and PR metadata before finalizing the sequence, because dependency bumps can have different risk levels depending on whether they touch only the version catalog or include wrapper/config changes.


gh pr view 97 --json number,title,headRefName,baseRefName,files,mergeStateStatus,isDraft,statusCheckRollup && gh pr view 98 --json number,title,headRefName,baseRefName,files,mergeStateStatus,isDraft,statusCheckRollup && gh pr view 99 --json number,title,headRefName,baseRefName,files,mergeStateStatus,isDraft,statusCheckRollup && gh pr view 100 --json number,title,headRefName,baseRefName,files,mergeStateStatus,isDraft,statusCheckRollup && gh pr view 101 --json number,title,headRefName,baseRefName,files,mergeStateStatus,isDraft,statusCheckRollup

Do you want to allow GitHub CLI network access so I can inspect the open Dependabot PR metadata for a precise merge plan?


Approved command prefix saved:
- ["gh", "pr", "view", "97", "--json", "number,title,headRefName,baseRefName,files,mergeStateStatus,isDraft,statusCheckRollup"]


# Sequential Dependabot PR Validation And Merge Plan

## Summary
Process the open Dependabot PRs one at a time in this order: `#97`, `#98`, `#99`, `#100`, `#101`.

For each PR: sync `master`, checkout the PR branch, rebase it onto the latest `origin/master`, run the required local validation gate, then either squash-merge it if green or fix/push and stop for review if red.

## Procedure
- Start each PR from a clean worktree:
  - `git checkout master`
  - `git pull --ff-only origin master`
  - `gh pr checkout <PR_NUMBER>`
  - `git rebase origin/master`
- Validate with the required gate:
  - Build/release checks:
    - `./gradlew assembleDebug assembleRelease`
  - JVM/CI-equivalent suite:
    - `./gradlew lintDebug ktlintCheck detekt verifyWithCoverage -Proborazzi.test.verify=true -Pcoverage`
  - GPU-accelerated smoke suite:
    - `./gradlew cleanManagedDevices`
    - `./gradlew pixel2api33Check -Pandroid.testoptions.manageddevices.emulator.gpu=host`
- If all commands pass:
  - Push the rebased branch if needed: `git push --force-with-lease`
  - Squash merge: `gh pr merge <PR_NUMBER> --squash --delete-branch`
  - Return to `master`, pull the merge, then move to the next PR.
- If any command fails:
  - Diagnose the failure on that PR branch.
  - Make the minimum scoped fix needed for that dependency bump.
  - Run the same validation gate again.
  - Commit with a focused message, push to the PR branch, and stop for your review.
  - After review approval and merge, resume with the next PR from fresh `master`.

## PR Order And Expected Risk
- `#97` AGP `8.9.1 -> 9.2.1`: highest risk; validate first because it can affect Gradle behavior, Android builds, lint, managed devices, and R8.
- `#98` Kotlin coroutines `1.10.2 -> 1.11.0`: medium risk; can affect async tests/runtime behavior.
- `#99` Compose BOM `2026.04.01 -> 2026.05.00`: medium risk; can affect UI, screenshots, and smoke flows.
- `#100` Detekt `1.23.6 -> 1.23.8`: low/medium risk; mainly static analysis behavior.
- `#101` DataStore preferences `1.1.1 -> 1.2.1`: medium risk; can affect setup/state persistence, so smoke is important.

## Test Acceptance Criteria
- No uncommitted changes before starting a PR.
- All validation commands complete successfully on the PR branch rebased onto current `origin/master`.
- `assembleRelease` must produce R8 output successfully.
- `verifyWithCoverage` must use the real `app/coverage-baselines/gmd_smoke.ec`, not an LFS pointer.
- Smoke tests must pass on `pixel2api33` using `gpu=host`.
- A failing PR is not merged until a fix is pushed, reviewed, and the full gate passes.

## Assumptions
- Merge strategy is squash merge.
- Local validation is the merge gate; the current GitHub PR checks are skipped because CI/smoke are manual-label workflows.
- Maintainer edits/pushes to Dependabot branches are allowed.
- If `gpu=host` fails due to local emulator/GPU infrastructure rather than product code, treat that as an environment blocker and do not merge until the smoke gate is rerun successfully.


<proposed_plan>
# Sequential Dependabot PR Validation And Merge Plan

## Summary
Process the open Dependabot PRs one at a time in this order: `#97`, `#98`, `#99`, `#100`, `#101`.

For each PR: sync `master`, checkout the PR branch, rebase it onto the latest `origin/master`, run the required local validation gate, then either squash-merge it if green or fix/push and stop for review if red.

## Procedure
- Start each PR from a clean worktree:
  - `git checkout master`
  - `git pull --ff-only origin master`
  - `gh pr checkout <PR_NUMBER>`
  - `git rebase origin/master`
- Validate with the required gate:
  - Build/release checks:
    - `./gradlew assembleDebug assembleRelease`
  - JVM/CI-equivalent suite:
    - `./gradlew lintDebug ktlintCheck detekt verifyWithCoverage -Proborazzi.test.verify=true -Pcoverage`
  - GPU-accelerated smoke suite:
    - `./gradlew cleanManagedDevices`
    - `./gradlew pixel2api33Check -Pandroid.testoptions.manageddevices.emulator.gpu=host`
- If all commands pass:
  - Push the rebased branch if needed: `git push --force-with-lease`
  - Squash merge: `gh pr merge <PR_NUMBER> --squash --delete-branch`
  - Return to `master`, pull the merge, then move to the next PR.
- If any command fails:
  - Diagnose the failure on that PR branch.
  - Make the minimum scoped fix needed for that dependency bump.
  - Run the same validation gate again.
  - Commit with a focused message, push to the PR branch, and stop for your review.
  - After review approval and merge, resume with the next PR from fresh `master`.

## PR Order And Expected Risk
- `#97` AGP `8.9.1 -> 9.2.1`: highest risk; validate first because it can affect Gradle behavior, Android builds, lint, managed devices, and R8.
- `#98` Kotlin coroutines `1.10.2 -> 1.11.0`: medium risk; can affect async tests/runtime behavior.
- `#99` Compose BOM `2026.04.01 -> 2026.05.00`: medium risk; can affect UI, screenshots, and smoke flows.
- `#100` Detekt `1.23.6 -> 1.23.8`: low/medium risk; mainly static analysis behavior.
- `#101` DataStore preferences `1.1.1 -> 1.2.1`: medium risk; can affect setup/state persistence, so smoke is important.

## Test Acceptance Criteria
- No uncommitted changes before starting a PR.
- All validation commands complete successfully on the PR branch rebased onto current `origin/master`.
- `assembleRelease` must produce R8 output successfully.
- `verifyWithCoverage` must use the real `app/coverage-baselines/gmd_smoke.ec`, not an LFS pointer.
- Smoke tests must pass on `pixel2api33` using `gpu=host`.
- A failing PR is not merged until a fix is pushed, reviewed, and the full gate passes.

## Assumptions
- Merge strategy is squash merge.
- Local validation is the merge gate; the current GitHub PR checks are skipped because CI/smoke are manual-label workflows.
- Maintainer edits/pushes to Dependabot branches are allowed.
- If `gpu=host` fails due to local emulator/GPU infrastructure rather than product code, treat that as an environment blocker and do not merge until the smoke gate is rerun successfully.
</proposed_plan>


<collaboration_mode># Collaboration Mode: Default

You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active.

Your active mode changes only when new developer instructions with a different `<collaboration_mode>...</collaboration_mode>` change it; user requests or tool descriptions do not change mode by themselves. Known mode names are Default and Plan.

## request_user_input availability

Use the `request_user_input` tool only when it is listed in the available tools for this turn.

In Default mode, strongly prefer making reasonable assumptions and executing the user's request rather than stopping to ask questions. If you absolutely must ask a question because the answer cannot be discovered from local context and a reasonable assumption would be risky, ask the user directly with a concise plain-text question. Never write a multiple choice question as a textual assistant message.
</collaboration_mode>


# Collaboration Mode: Default

You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active.

Your active mode changes only when new developer instructions with a different `<collaboration_mode>...</collaboration_mode>` change it; user requests or tool descriptions do not change mode by themselves. Known mode names are Default and Plan.

## request_user_input availability

Use the `request_user_input` tool only when it is listed in the available tools for this turn.

In Default mode, strongly prefer making reasonable assumptions and executing the user's request rather than stopping to ask questions. If you absolutely must ask a question because the answer cannot be discovered from local context and a reasonable assumption would be risky, ask the user directly with a concise plain-text question. Never write a multiple choice question as a textual assistant message.


Implement the plan.


I’ll start with PR `#97` and keep the work strictly sequential. First I’m checking the current worktree and syncing `master`, then I’ll validate that branch before touching the next PR.


Validate and resolve PR #98 coroutines bump

Validate and resolve PR #99 Compose BOM bump

Validate and resolve PR #100 Detekt bump

Validate and resolve PR #101 DataStore bump


Output: Plan updated


Output:
Chunk ID: 41406e
Wall time: 0.2035 seconds
Process exited with code 0
Original token count: 7
Output:
## master...origin/master


Output:
Chunk ID: b20b66
Wall time: 0.2010 seconds
Process exited with code 0
Original token count: 25
Output:
origin	git@github.com:yorvana/android.git (fetch)
origin	git@github.com:yorvana/android.git (push)


Do you want to allow Git network access so I can sync master before validating and merging Dependabot PRs?


Output:
Chunk ID: 45ce29
Wall time: 1.8375 seconds
Process exited with code 0
Original token count: 25
Output:
From github.com:yorvana/android
 * branch            master     -> FETCH_HEAD
Already up to date.


`master` is clean and up to date. I’m checking out PR `#97` now and will rebase it onto the current base before running the validation gate.


Do you want to allow GitHub CLI network access to checkout Dependabot PR #97?


Approved command prefix saved:
- ["gh", "pr", "checkout"]


Output:
Chunk ID: cf08e6
Wall time: 2.6238 seconds
Process exited with code 0
Original token count: 92
Output:
From github.com:yorvana/android
 * [new branch]      dependabot/gradle/com.android.application-9.2.1 -> origin/dependabot/gradle/com.android.application-9.2.1
Switched to a new branch 'dependabot/gradle/com.android.application-9.2.1'
branch 'dependabot/gradle/com.android.application-9.2.1' set up to track 'origin/dependabot/gradle/com.android.application-9.2.1'.


Output:
Chunk ID: d2bc4b
Wall time: 0.1262 seconds
Process exited with code 0
Original token count: 89
Output:
error: cannot lock ref 'REBASE_HEAD': Unable to create '/home/Messier82/projects/service-book/.git/REBASE_HEAD.lock': Read-only file system
Current branch dependabot/gradle/com.android.application-9.2.1 is up to date.
error: cannot lock ref 'AUTO_MERGE': Unable to create '/home/Messier82/projects/service-book/.git/AUTO_MERGE.lock': Read-only file system


The rebase is a no-op logically, but the sandbox blocked Git from writing lock files under `.git`. I’ll rerun Git operations that mutate repository metadata with escalated permissions.


Do you want to allow Git to update repository metadata for rebasing the PR branch?


Approved command prefix saved:
- ["git", "rebase"]


Output:
Chunk ID: 1cb204
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 20
Output:
Current branch dependabot/gradle/com.android.application-9.2.1 is up to date.


PR `#97` is current with `master`. I’m starting the build/release validation first; this is the AGP bump, so failures here are the most likely and most informative.


Output:
Chunk ID: 418884
Wall time: 0.0000 seconds
Process exited with code 1
Original token count: 178
Output:
Picked up JAVA_TOOL_OPTIONS: -Xlog:os+container=off
Exception in thread "main" java.io.FileNotFoundException: /home/Messier82/.gradle/wrapper/dists/gradle-9.5.1-bin/iq79hdu3mqx29lgffhp8bfmx/gradle-9.5.1-bin.zip.lck (Read-only file system)
	at java.base/java.io.RandomAccessFile.open0(Native Method)
	at java.base/java.io.RandomAccessFile.open(RandomAccessFile.java:356)
	at java.base/java.io.RandomAccessFile.<init>(RandomAccessFile.java:273)
	at java.base/java.io.RandomAccessFile.<init>(RandomAccessFile.java:223)
	at org.gradle.wrapper.Install.createDist(SourceFile:24)
	at org.gradle.wrapper.GradleWrapperMain.lambda$prepareWrapper$0(SourceFile:2)
	at org.gradle.wrapper.GradleWrapperMain.main(SourceFile:2)


Gradle needs to write wrapper/cache files under `~/.gradle`, which is outside the writable sandbox. I’ll rerun the validation commands with escalated filesystem access where needed.


Do you want to allow Gradle to write its cache under ~/.gradle while building PR #97?


Approved command prefix saved:
- ["./gradlew"]


Output:
Chunk ID: 55f1f7
Wall time: 30.0009 seconds
Process running with session ID 43890
Original token count: 37
Output:
Picked up JAVA_TOOL_OPTIONS: -Xlog:os+container=off
Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details


The AGP build is still running under Gradle. I’m waiting for completion before deciding whether to continue into the JVM and smoke gates.


Output:
Chunk ID: 0fbedb
Wall time: 27.3687 seconds
Process exited with code 1
Original token count: 277
Output:

> Configure project :app
WARNING: The option setting 'android.testoptions.manageddevices.emulator.gpu=host' is experimental.

FAILURE: Build failed with an exception.

* Where:
Build file '/home/Messier82/projects/service-book/app/build.gradle.kts' line: 6

* What went wrong:
An exception occurred applying plugin request [id: 'org.jetbrains.kotlin.android', version: '2.3.21']
> Failed to apply plugin 'org.jetbrains.kotlin.android'.
   > ⛔ Failed to apply plugin 'org.jetbrains.kotlin.android'
     The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin support since AGP 9.0.
     Solution: Remove the 'org.jetbrains.kotlin.android' plugin from this project's build file: app/build.gradle.kts.
     See https://kotl.in/gradle/agp-built-in-kotlin for more details.
      > java.lang.Throwable (no error message)

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights from a Build Scan (powered by Develocity).
> Get more help at https://help.gradle.org.

BUILD FAILED in 59s


PR `#97` fails immediately because AGP 9 rejects applying `org.jetbrains.kotlin.android`; Kotlin support is built into AGP now. I’m going to make the minimal AGP-9 compatibility fix by removing the Android Kotlin plugin application from the app module, then rerun the same gate.


Input:
*** Begin Patch
*** Update File: /home/Messier82/projects/service-book/app/build.gradle.kts
@@
 plugins {
     alias(libs.plugins.android.application)
-    alias(libs.plugins.kotlin.android)
     alias(libs.plugins.kotlin.serialization)
     alias(libs.plugins.compose.compiler)
     alias(libs.plugins.roborazzi)
*** End Patch


Success. Updated the following files:
M /home/Messier82/projects/service-book/app/build.gradle.kts

@@ -7,3 +7,2 @@
     alias(libs.plugins.android.application)
-    alias(libs.plugins.kotlin.android)
     alias(libs.plugins.kotlin.serialization)


Output: {"output":"Success. Updated the following files:\nM /home/Messier82/projects/service-book/app/build.gradle.kts\n","metadata":{"exit_code":0,"duration_seconds":0.9}}


The first AGP 9 compatibility edit is in place. I’m rerunning the build to see whether AGP surfaces the next required migration or if this clears the build phase.


Output:
Chunk ID: debc13
Wall time: 30.0008 seconds
Process running with session ID 18346
Original token count: 67
Output:
Picked up JAVA_TOOL_OPTIONS: -Xlog:os+container=off
Checking the license for package Android SDK Build-Tools 36 in /home/Messier82/Android/Sdk/licenses
License for package Android SDK Build-Tools 36 accepted.
Preparing "Install Android SDK Build-Tools 36 v.36.0.0".


The rerun progressed past plugin application and is installing/using Android SDK build tools needed by the upgraded AGP. I’m letting it finish before moving to tests.


Output:
Chunk ID: 86d654
Wall time: 30.0006 seconds
Process running with session ID 18346
Original token count: 213
Output:
"Install Android SDK Build-Tools 36 v.36.0.0" ready.
Installing Android SDK Build-Tools 36 in /home/Messier82/Android/Sdk/build-tools/36.0.0
"Install Android SDK Build-Tools 36 v.36.0.0" complete.
"Install Android SDK Build-Tools 36 v.36.0.0" finished.
[=========                              ] 25%
> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:mergeDebugNativeDebugMetadata NO-SOURCE
> Task :app:generateDebugBuildConfig
> Task :app:generateDebugResources
> Task :app:packageDebugResources
> Task :app:processDebugNavigationResources
> Task :app:generateDebugAssets UP-TO-DATE
> Task :app:javaPreCompileDebug
> Task :app:parseDebugLocalResources
> Task :app:mergeDebugAssets
> Task :app:compressDebugAssets
> Task :app:generateDebugRFile
> Task :app:generateDebugGlobalSynthetics


Output:
Chunk ID: 25bc24
Wall time: 30.0012 seconds
Process running with session ID 18346
Original token count: 875
Output:
> Task :app:desugarDebugFileDependencies
> Task :app:checkDebugDuplicateClasses
> Task :app:checkDebugAarMetadata

> Task :app:compileDebugKotlin
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/ImageViewerScreen.kt:60:9 'fun rememberTransformableState(onTransformation: (Float, Offset, Float) -> Unit): TransformableState' is deprecated. Prefer remembering a TransformableState with a onTransformation lambda that takes the centroid. This centroid (if specified) is the point at which zooming or rotation should happen around which allows for more natural transformations.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:151:60 'fun rememberTooltipPositionProvider(spacingBetweenTooltipAndAnchor: Dp = ...): PopupPositionProvider' is deprecated. Deprecated in favor of rememberTooltipPositionProvider API that takes a preferred positioning. Please use rememberTooltipPositionProvider with TooltipAnchorPosition.Above if this same behavior is desired.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:183:60 'fun rememberTooltipPositionProvider(spacingBetweenTooltipAndAnchor: Dp = ...): PopupPositionProvider' is deprecated. Deprecated in favor of rememberTooltipPositionProvider API that takes a preferred positioning. Please use rememberTooltipPositionProvider with TooltipAnchorPosition.Above if this same behavior is desired.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:410:49 'val Icons.Filled.InsertDriveFile: ImageVector' is deprecated. Use the AutoMirrored version at Icons.AutoMirrored.Filled.InsertDriveFile.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:482:64 'fun rememberTooltipPositionProvider(spacingBetweenTooltipAndAnchor: Dp = ...): PopupPositionProvider' is deprecated. Deprecated in favor of rememberTooltipPositionProvider API that takes a preferred positioning. Please use rememberTooltipPositionProvider with TooltipAnchorPosition.Above if this same behavior is desired.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:489:64 'fun rememberTooltipPositionProvider(spacingBetweenTooltipAndAnchor: Dp = ...): PopupPositionProvider' is deprecated. Deprecated in favor of rememberTooltipPositionProvider API that takes a preferred positioning. Please use rememberTooltipPositionProvider with TooltipAnchorPosition.Above if this same behavior is desired.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:580:107 'val Icons.Filled.InsertDriveFile: ImageVector' is deprecated. Use the AutoMirrored version at Icons.AutoMirrored.Filled.InsertDriveFile.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/util/SentryWrapper.kt:5:8 'class UserFeedback : Any, JsonUnknown, JsonSerializable' is deprecated. Deprecated in Java.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/util/SentryWrapper.kt:56:17 'constructor(p0: SentryId!): UserFeedback' is deprecated. Deprecated in Java.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/util/SentryWrapper.kt:60:20 'static fun captureUserFeedback(p0: UserFeedback): Unit' is deprecated. Deprecated in Java.

> Task :app:mergeExtDexDebug


Output:
Chunk ID: 852dc8
Wall time: 30.0009 seconds
Process running with session ID 18346
Original token count: 0
Output:


The AGP build has reached Kotlin compilation and packaging work with warnings only so far. I’m still waiting on final debug/release build status.


Output:
Chunk ID: f76f48
Wall time: 30.0014 seconds
Process running with session ID 18346
Original token count: 477
Output:

Picked up JAVA_TOOL_OPTIONS: -Xlog:os+container=off

> Task :app:compileDebugJavaWithJavac
Picked up JAVA_TOOL_OPTIONS: -Xlog:os+container=off

Picked up JAVA_TOOL_OPTIONS: -Xlog:os+container=off

> Task :app:processDebugJavaRes
> Task :app:mergeLibDexDebug
> Task :app:mapDebugSourceSetPaths
> Task :app:mergeDebugJavaResource
> Task :app:compileDebugNavigationResources
> Task :app:createDebugCompatibleScreenManifests
> Task :app:extractDeepLinksDebug
> Task :app:mergeDebugResources
> Task :app:processDebugMainManifest
> Task :app:processDebugManifest
> Task :app:mergeDebugJniLibFolders
> Task :app:mergeDebugNativeLibs
> Task :app:validateSigningDebug
> Task :app:processDebugManifestForPackage

> Task :app:stripDebugDebugSymbols
Unable to strip the following libraries, packaging them as they are: libandroidx.graphics.path.so, libdatastore_shared_counter.so, libsentry-android.so, libsentry.so. Run with --info option to learn more.

> Task :app:writeDebugAppMetadata
> Task :app:writeDebugSigningConfigVersions
> Task :app:preReleaseBuild UP-TO-DATE
> Task :app:mergeReleaseJniLibFolders
> Task :app:mergeReleaseNativeLibs
> Task :app:checkReleaseDuplicateClasses

> Task :app:stripReleaseDebugSymbols
Unable to strip the following libraries, packaging them as they are: libandroidx.graphics.path.so, libdatastore_shared_counter.so, libsentry-android.so, libsentry.so. Run with --info option to learn more.

> Task :app:generateReleaseBuildConfig
> Task :app:extractReleaseNativeSymbolTables
> Task :app:generateReleaseResources FROM-CACHE
> Task :app:mergeReleaseNativeDebugMetadata NO-SOURCE
> Task :app:packageReleaseResources
> Task :app:processReleaseNavigationResources FROM-CACHE
> Task :app:parseReleaseLocalResources FROM-CACHE
> Task :app:generateReleaseRFile FROM-CACHE
> Task :app:javaPreCompileRelease FROM-CACHE
> Task :app:processDebugResources
> Task :app:mergeReleaseArtProfile


Output:
Chunk ID: 3e4a1b
Wall time: 7.2207 seconds
Process exited with code 1
Original token count: 1356
Output:
> Task :app:dexBuilderDebug
> Task :app:checkReleaseAarMetadata
> Task :app:mapReleaseSourceSetPaths
> Task :app:compileReleaseNavigationResources FROM-CACHE
> Task :app:mergeProjectDexDebug
> Task :app:createReleaseCompatibleScreenManifests
> Task :app:extractDeepLinksRelease FROM-CACHE
> Task :app:processReleaseMainManifest
> Task :app:processReleaseManifest
> Task :app:processReleaseManifestForPackage
> Task :app:extractProguardFiles
> Task :app:mergeReleaseStartupProfile
> Task :app:generateReleaseAssets UP-TO-DATE
> Task :app:mergeReleaseAssets
> Task :app:compressReleaseAssets
> Task :app:mergeReleaseResources
> Task :app:extractReleaseVersionControlInfo
> Task :app:collectReleaseDependencies
> Task :app:processReleaseResources
> Task :app:sdkReleaseDependencyData
> Task :app:validateSigningRelease FAILED
> Task :app:optimizeReleaseResources
> Task :app:packageDebug
> Task :app:createDebugApkListingFileRedirect

> Task :app:compileReleaseKotlin
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/ImageViewerScreen.kt:60:9 'fun rememberTransformableState(onTransformation: (Float, Offset, Float) -> Unit): TransformableState' is deprecated. Prefer remembering a TransformableState with a onTransformation lambda that takes the centroid. This centroid (if specified) is the point at which zooming or rotation should happen around which allows for more natural transformations.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:151:60 'fun rememberTooltipPositionProvider(spacingBetweenTooltipAndAnchor: Dp = ...): PopupPositionProvider' is deprecated. Deprecated in favor of rememberTooltipPositionProvider API that takes a preferred positioning. Please use rememberTooltipPositionProvider with TooltipAnchorPosition.Above if this same behavior is desired.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:183:60 'fun rememberTooltipPositionProvider(spacingBetweenTooltipAndAnchor: Dp = ...): PopupPositionProvider' is deprecated. Deprecated in favor of rememberTooltipPositionProvider API that takes a preferred positioning. Please use rememberTooltipPositionProvider with TooltipAnchorPosition.Above if this same behavior is desired.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:410:49 'val Icons.Filled.InsertDriveFile: ImageVector' is deprecated. Use the AutoMirrored version at Icons.AutoMirrored.Filled.InsertDriveFile.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:482:64 'fun rememberTooltipPositionProvider(spacingBetweenTooltipAndAnchor: Dp = ...): PopupPositionProvider' is deprecated. Deprecated in favor of rememberTooltipPositionProvider API that takes a preferred positioning. Please use rememberTooltipPositionProvider with TooltipAnchorPosition.Above if this same behavior is desired.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:489:64 'fun rememberTooltipPositionProvider(spacingBetweenTooltipAndAnchor: Dp = ...): PopupPositionProvider' is deprecated. Deprecated in favor of rememberTooltipPositionProvider API that takes a preferred positioning. Please use rememberTooltipPositionProvider with TooltipAnchorPosition.Above if this same behavior is desired.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:580:107 'val Icons.Filled.InsertDriveFile: ImageVector' is deprecated. Use the AutoMirrored version at Icons.AutoMirrored.Filled.InsertDriveFile.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/util/SentryWrapper.kt:5:8 'class UserFeedback : Any, JsonUnknown, JsonSerializable' is deprecated. Deprecated in Java.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/util/SentryWrapper.kt:56:17 'constructor(p0: SentryId!): UserFeedback' is deprecated. Deprecated in Java.
w: file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/util/SentryWrapper.kt:60:20 'static fun captureUserFeedback(p0: UserFeedback): Unit' is deprecated. Deprecated in Java.

[Incubating] Problems report is available at: file:///home/Messier82/projects/service-book/build/reports/problems/problems-report.html

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:validateSigningRelease' (registered by plugin 'com.android.internal.application').
> Keystore file '/home/Messier82/projects/service-book/app/upload.jks' not found for signing config 'release'.

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights from a Build Scan (powered by Develocity).
> Get more help at
Deprecated Gradle features were used in this build, making it incompatible with Gradle 10.
https://help.gradle.org.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/9.5.1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD FAILED71 actionable tasks: 64 executed, 7 from cache in 2m 51s


The AGP build now fails only at release signing because this machine has a local ignored `app/keystore.properties` pointing to `app/upload.jks`, but the keystore file is not present. That is a local environment issue, so I’m checking the ignored keystore config and will temporarily move it out of the way for unsigned release validation, then restore it after the PR run.


ls -l app/keystore.properties app/upload.jks


git status --short --ignored app/keystore.properties app/upload.jks


Output:
Chunk ID: e3a233
Wall time: 0.2426 seconds
Process exited with code 2
Original token count: 34
Output:
ls: cannot access 'app/upload.jks': No such file or directory
-rw-r--r--. 1 Messier82 Messier82 91 Apr  9 16:03 app/keystore.properties


Output:
Chunk ID: 0ad9ac
Wall time: 0.2275 seconds
Process exited with code 0
Original token count: 7
Output:
!! app/keystore.properties


Output:
Chunk ID: 881cda
Wall time: 0.1175 seconds
Process exited with code 0
Original token count: 0
Output:


The missing-keystore local config is parked in `/tmp/service-book-keystore.properties.bak` so `assembleRelease` follows the repo’s unsigned-release path, matching CI. I’m rerunning the build gate for `#97`.


Output:
Chunk ID: b4d33e
Wall time: 30.0007 seconds
Process running with session ID 6672
Original token count: 992
Output:
Picked up JAVA_TOOL_OPTIONS: -Xlog:os+container=off

> Configure project :app
WARNING: The option setting 'android.testoptions.manageddevices.emulator.gpu=host' is experimental.

> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:mergeDebugNativeDebugMetadata NO-SOURCE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:generateDebugResources UP-TO-DATE
> Task :app:packageDebugResources UP-TO-DATE
> Task :app:processDebugNavigationResources UP-TO-DATE
> Task :app:parseDebugLocalResources UP-TO-DATE
> Task :app:generateDebugRFile UP-TO-DATE
> Task :app:compileDebugKotlin UP-TO-DATE
> Task :app:javaPreCompileDebug UP-TO-DATE
> Task :app:compileDebugJavaWithJavac UP-TO-DATE
> Task :app:generateDebugAssets UP-TO-DATE
> Task :app:mergeDebugAssets UP-TO-DATE
> Task :app:compressDebugAssets UP-TO-DATE
> Task :app:generateDebugGlobalSynthetics UP-TO-DATE
> Task :app:processDebugJavaRes UP-TO-DATE
> Task :app:mergeDebugJavaResource UP-TO-DATE
> Task :app:checkDebugDuplicateClasses UP-TO-DATE
> Task :app:desugarDebugFileDependencies UP-TO-DATE
> Task :app:mergeExtDexDebug UP-TO-DATE
> Task :app:mergeLibDexDebug UP-TO-DATE
> Task :app:checkDebugAarMetadata UP-TO-DATE
> Task :app:mapDebugSourceSetPaths UP-TO-DATE
> Task :app:compileDebugNavigationResources UP-TO-DATE
> Task :app:mergeDebugResources UP-TO-DATE
> Task :app:createDebugCompatibleScreenManifests UP-TO-DATE
> Task :app:extractDeepLinksDebug UP-TO-DATE
> Task :app:processDebugMainManifest UP-TO-DATE
> Task :app:processDebugManifest UP-TO-DATE
> Task :app:processDebugManifestForPackage UP-TO-DATE
> Task :app:processDebugResources UP-TO-DATE
> Task :app:dexBuilderDebug UP-TO-DATE
> Task :app:mergeProjectDexDebug UP-TO-DATE
> Task :app:mergeDebugJniLibFolders UP-TO-DATE
> Task :app:mergeDebugNativeLibs UP-TO-DATE
> Task :app:stripDebugDebugSymbols UP-TO-DATE
> Task :app:validateSigningDebug UP-TO-DATE
> Task :app:writeDebugAppMetadata UP-TO-DATE
> Task :app:writeDebugSigningConfigVersions UP-TO-DATE
> Task :app:packageDebug UP-TO-DATE
> Task :app:createDebugApkListingFileRedirect UP-TO-DATE
> Task :app:assembleDebug UP-TO-DATE
> Task :app:preReleaseBuild UP-TO-DATE
> Task :app:mergeReleaseJniLibFolders UP-TO-DATE
> Task :app:mergeReleaseNativeLibs UP-TO-DATE
> Task :app:stripReleaseDebugSymbols UP-TO-DATE
> Task :app:extractReleaseNativeSymbolTables UP-TO-DATE
> Task :app:mergeReleaseNativeDebugMetadata NO-SOURCE
> Task :app:checkReleaseDuplicateClasses UP-TO-DATE
> Task :app:generateReleaseBuildConfig UP-TO-DATE
> Task :app:generateReleaseResources UP-TO-DATE
> Task :app:packageReleaseResources UP-TO-DATE
> Task :app:processReleaseNavigationResources UP-TO-DATE
> Task :app:parseReleaseLocalResources UP-TO-DATE
> Task :app:generateReleaseRFile UP-TO-DATE
> Task :app:compileReleaseKotlin UP-TO-DATE
> Task :app:javaPreCompileRelease UP-TO-DATE
> Task :app:compileReleaseJavaWithJavac
> Task :app:mergeReleaseArtProfile UP-TO-DATE
> Task :app:checkReleaseAarMetadata UP-TO-DATE
> Task :app:mapReleaseSourceSetPaths UP-TO-DATE
> Task :app:compileReleaseNavigationResources UP-TO-DATE
> Task :app:mergeReleaseResources UP-TO-DATE
> Task :app:createReleaseCompatibleScreenManifests
> Task :app:extractDeepLinksRelease UP-TO-DATE
> Task :app:processReleaseMainManifest UP-TO-DATE
> Task :app:processReleaseManifest
> Task :app:processReleaseManifestForPackage UP-TO-DATE
> Task :app:extractProguardFiles UP-TO-DATE
> Task :app:processReleaseJavaRes
> Task :app:mergeReleaseStartupProfile UP-TO-DATE
> Task :app:generateReleaseAssets UP-TO-DATE
> Task :app:mergeReleaseAssets UP-TO-DATE
> Task :app:compressReleaseAssets UP-TO-DATE
> Task :app:extractReleaseVersionControlInfo UP-TO-DATE
> Task :app:processReleaseResources
> Task :app:generateReleaseLintVitalReportModel
> Task :app:mergeReleaseGeneratedProguardFiles
> Task :app:mergeReleaseJavaResource
> Task :app:expandReleaseArtProfileWildcards
> Task :app:produceReleaseComposeMapping


Output:
Chunk ID: d44e91
Wall time: 30.0009 seconds
Process running with session ID 6672
Original token count: 75
Output:
> Task :app:lintVitalAnalyzeRelease
> Task :app:reportReleaseComposeMappingErrors
> Task :app:collectReleaseDependencies UP-TO-DATE
> Task :app:sdkReleaseDependencyData UP-TO-DATE
> Task :app:writeReleaseAppMetadata
> Task :app:writeReleaseSigningConfigVersions
> Task :app:optimizeReleaseResources


Output:
Chunk ID: 2f6354
Wall time: 30.0009 seconds
Process running with session ID 6672
Original token count: 8
Output:
> Task :app:minifyReleaseWithR8


With the local signing issue removed, `#97` is now through debug build and deep into release/R8. I’m waiting on the final build result before starting the JVM checks.


Output:
Chunk ID: 26dc2f
Wall time: 30.0007 seconds
Process running with session ID 6672
Original token count: 107
Output:

> Task :app:lintVitalReportRelease
Lint found no errors or warnings

75 errors/warnings were listed in the baseline file (/home/Messier82/projects/service-book/app/lint-baseline.xml) but not found in the project; perhaps they have been fixed?

Note: The baseline was created using a different target/variant than it was checked against.
Creation variant: lint
Current variant: lintVitalRelease

> Task :app:lintVitalRelease


Output:
Chunk ID: 5bf48c
Wall time: 28.8405 seconds
Process exited with code 0
Original token count: 229
Output:
> Task :app:mergeReleaseComposeMapping
> Task :app:compileReleaseArtProfile
> Task :app:packageRelease
> Task :app:createReleaseApkListingFileRedirect
> Task :app:assembleRelease

[Incubating] Problems report is available at: file:///home/Messier82/projects/service-book/build/reports/problems/problems-report.html

Deprecated Gradle features were used in this build, making it incompatible with Gradle 10.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/9.5.1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD SUCCESSFUL in 2m 41s
88 actionable tasks: 22 executed, 66 up-to-date
Consider enabling configuration cache to speed up this build: https://docs.gradle.org/9.5.1/userguide/configuration_cache_enabling.html


The build/release phase for `#97` passes after the AGP compatibility fix. I’m running the CI-equivalent lint/static/JVM/coverage gate next.


./gradlew lintDebug ktlintCheck detekt verifyWithCoverage -Proborazzi.test.verify=true -Pcoverage


Output:
Chunk ID: 0c7824
Wall time: 30.0011 seconds
Process running with session ID 43131
Original token count: 635
Output:
Picked up JAVA_TOOL_OPTIONS: -Xlog:os+container=off

> Configure project :app
WARNING: The option setting 'android.testoptions.manageddevices.emulator.gpu=host' is experimental.

> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:generateDebugResources UP-TO-DATE
> Task :app:packageDebugResources UP-TO-DATE
> Task :app:processDebugNavigationResources UP-TO-DATE
> Task :app:parseDebugLocalResources UP-TO-DATE
> Task :app:generateDebugRFile UP-TO-DATE
> Task :app:compileDebugKotlin UP-TO-DATE
> Task :app:javaPreCompileDebug UP-TO-DATE
> Task :app:compileDebugJavaWithJavac UP-TO-DATE
> Task :app:checkDebugAarMetadata UP-TO-DATE
> Task :app:mapDebugSourceSetPaths UP-TO-DATE
> Task :app:compileDebugNavigationResources UP-TO-DATE
> Task :app:mergeDebugResources UP-TO-DATE
> Task :app:createDebugCompatibleScreenManifests UP-TO-DATE
> Task :app:extractDeepLinksDebug UP-TO-DATE
> Task :app:processDebugMainManifest UP-TO-DATE
> Task :app:processDebugManifest UP-TO-DATE
> Task :app:processDebugManifestForPackage UP-TO-DATE
> Task :app:processDebugResources UP-TO-DATE
> Task :app:preDebugAndroidTestBuild SKIPPED
> Task :app:extractProguardFiles UP-TO-DATE
> Task :app:bundleDebugClassesToCompileJar
> Task :app:generateDebugLintReportModel
> Task :app:generateDebugAndroidTestLintModel
> Task :app:preDebugUnitTestBuild UP-TO-DATE
> Task :app:generateDebugUnitTestLintModel
> Task :app:loadKtlintReporters

> Task :app:runKtlintCheckOverKotlinScripts
[0.004s][warning][os,container] Cgroup memory controller path at '/sys/fs/cgroup' seems to have moved to '/../../../app.slice/app-tmux.slice/tmux-spawn-c63a1dc5-17fd-4bb1-b0d0-af4c7f1bdeb9.scope', detected limits won't be accurate
[0.004s][warning][os,container] Cgroup cpu controller path at '/sys/fs/cgroup' seems to have moved to '/../../../app.slice/app-tmux.slice/tmux-spawn-c63a1dc5-17fd-4bb1-b0d0-af4c7f1bdeb9.scope', detected limits won't be accurate

> Task :app:ktlintKotlinScriptCheck
> Task :app:ktlintCheck
> Task :app:lintAnalyzeDebugAndroidTest
> Task :app:detekt
> Task :app:bundleDebugClassesToRuntimeJar
> Task :app:javaPreCompileDebugUnitTest FROM-CACHE
> Task :app:generateDebugAssets UP-TO-DATE
> Task :app:mergeDebugAssets UP-TO-DATE
> Task :app:packageDebugUnitTestForUnitTest
> Task :app:processDebugUnitTestManifest
> Task :app:generateDebugUnitTestConfig
> Task :app:generateDebugJacocoPropertiesFile
> Task :app:processDebugJavaRes
> Task :app:restoreOutputDirRoborazzi SKIPPED


Output:
Chunk ID: 0ca80c
Wall time: 30.0003 seconds
Process running with session ID 43131
Original token count: 6196
Output:

> Task :app:compileDebugUnitTestKotlin
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/MainActivityTest.kt:27:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/components/ComponentScreenshotTest.kt:20:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/components/PaywallInfoSheetScreenshotTest.kt:18:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/components/PaywallScaffoldScreenshotTest.kt:25:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/components/PaywallStatusCardScreenshotTest.kt:20:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/components/RequiredLabelTest.kt:27:27 'fun <reified A : ComponentActivity> createAndroidComposeRule(effectContext: CoroutineContext = ...): AndroidComposeTestRule<ActivityScenarioRule<A>, A>' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createAndroidComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/components/UpgradeDialogScreenshotTest.kt:11:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/components/UpgradeDialogTest.kt:19:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/navigation/SetupScreenTest.kt:17:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/navigation/SetupScreenshotTest.kt:15:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenIT.kt:27:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenTest.kt:26:27 'fun <reified A : ComponentActivity> createAndroidComposeRule(effectContext: CoroutineContext = ...): AndroidComposeTestRule<ActivityScenarioRule<A>, A>' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createAndroidComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/records/AddEditRecordScreenshotTest.kt:15:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/records/ImageViewerScreenshotTest.kt:15:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/records/RecordDetailScreenIT.kt:23:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/records/RecordDetailScreenshotTest.kt:16:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenIT.kt:28:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenTest.kt:30:27 'fun <reified A : ComponentActivity> createAndroidComposeRule(effectContext: CoroutineContext = ...): AndroidComposeTestRule<ActivityScenarioRule<A>, A>' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createAndroidComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/records/VehicleDetailScreenshotTest.kt:19:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/reportbug/ReportBugScreenTest.kt:30:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/reportbug/ReportBugScreenshotTest.kt:21:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/reportbug/ReportBugViewModelTest.kt:232:39 No cast needed.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/reportbug/ReportBugViewModelTest.kt:261:39 No cast needed.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/reportbug/ReportBugViewModelTest.kt:307:39 No cast needed.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/CategoriesScreenIT.kt:27:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/CategoriesScreenTest.kt:24:27 'fun <reified A : ComponentActivity> createAndroidComposeRule(effectContext: CoroutineContext = ...): AndroidComposeTestRule<ActivityScenarioRule<A>, A>' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createAndroidComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/CategoriesScreenshotTest.kt:18:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsScreenIT.kt:27:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsScreenshotTest.kt:24:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:371:13 The corresponding parameter in the supertype 'VaultStorage' is named 'vehicleId'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:372:13 The corresponding parameter in the supertype 'VaultStorage' is named 'recordId'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:378:13 The corresponding parameter in the supertype 'VaultStorage' is named 'vehicleId'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:379:13 The corresponding parameter in the supertype 'VaultStorage' is named 'recordId'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:383:13 The corresponding parameter in the supertype 'VaultStorage' is named 'vehicleId'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:384:13 The corresponding parameter in the supertype 'VaultStorage' is named 'recordId'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:385:13 The corresponding parameter in the supertype 'VaultStorage' is named 'sourceUri'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:386:13 The corresponding parameter in the supertype 'VaultStorage' is named 'filename'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:390:13 The corresponding parameter in the supertype 'VaultStorage' is named 'vehicleId'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:391:13 The corresponding parameter in the supertype 'VaultStorage' is named 'recordId'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:392:13 The corresponding parameter in the supertype 'VaultStorage' is named 'filename'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:396:13 The corresponding parameter in the supertype 'VaultStorage' is named 'vehicleId'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:397:13 The corresponding parameter in the supertype 'VaultStorage' is named 'recordId'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/settings/SettingsViewModelIT.kt:398:13 The corresponding parameter in the supertype 'VaultStorage' is named 'filename'. This may cause problems when calling this function with named arguments.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenIT.kt:26:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenTest.kt:26:27 'fun <reified A : ComponentActivity> createAndroidComposeRule(effectContext: CoroutineContext = ...): AndroidComposeTestRule<ActivityScenarioRule<A>, A>' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createAndroidComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/vehicles/AddEditVehicleScreenshotTest.kt:16:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/vehicles/VehicleListConsentDialogTest.kt:34:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/vehicles/VehicleListScreenIT.kt:30:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/vehicles/VehicleListScreenshotTest.kt:15:27 'fun createComposeRule(effectContext: CoroutineContext = ...): ComposeContentTestRule' is deprecated. Use `androidx.compose.ui.test.junit4.v2.createComposeRule` instead. The v2 APIs use StandardTestDispatcher instead of UnconfinedTestDispatcher, which aligns with standard coroutine behavior by queuing tasks rather than executing them immediately. Tests relying on immediate execution may require explicit synchronization. Please refer to the migration guide for more details.
w: file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/util/SentryWrapperTest.kt:57:20 'static fun captureUserFeedback(p0: UserFeedback): Unit' is deprecated. Deprecated in Java.

> Task :app:compileDebugUnitTestJavaWithJavac NO-SOURCE
> Task :app:processDebugUnitTestJavaRes
> Task :app:lintAnalyzeDebug
> Task :app:lintAnalyzeDebugUnitTest
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
Picked up JAVA_TOOL_OPTIONS: -Xlog:os+container=off
Picked up JAVA_TOOL_OPTIONS: -Xlog:os+container=off

> Task :app:testDebugUnitTest

OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended


The JVM gate is running through unit tests now. The compiler warnings are pre-existing API deprecations/noise; no failure yet.


Output:
Chunk ID: 443d23
Wall time: 30.0008 seconds
Process running with session ID 43131
Original token count: 65
Output:
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended


Output:
Chunk ID: f68f74
Wall time: 30.0014 seconds
Process running with session ID 43131
Original token count: 68
Output:

> Task :app:testDebugUnitTest

SetupScreenshotTest > setup_step1_folder FAILED
    java.lang.AssertionError at SetupScreenshotTest.kt:24
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended


The JVM gate has one failing screenshot verification so far: `SetupScreenshotTest.setup_step1_folder`. I’ll let the whole suite finish so we get the complete failure set before changing anything.


Output: aborted by user after 25.4s


<turn_aborted>
The user interrupted the previous turn on purpose. Any running unified exec processes may still be running in the background. If any tools/commands were aborted, they may have partially executed.
</turn_aborted>


Screenshot test failed most likely because the app was renamed prior to release, so that one place, where the name is shown on screen is now changed. We, most likely, just need to record new screenshots


That matches the failure shape: a screenshot assertion on the setup screen after visible app-name text changed. I’ll first check whether the JVM suite has finished and then record only the affected Roborazzi snapshot(s), not all screenshots blindly.


Output:
Chunk ID: fa48c7
Wall time: 0.0000 seconds
Process exited with code 1
Original token count: 265
Output:

442 tests completed, 3 failed

> Task :app:testDebugUnitTest FAILED
> Task :app:finalizeTestRoborazziDebug

[Incubating] Problems report is available at: file:///home/Messier82/projects/service-book/build/reports/problems/problems-report.html
FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:testDebugUnitTest'.
> There were failing tests. See the results at: file:///home/Messier82/projects/service-book/app/build/test-results/testDebugUnitTest/

* Try:
> Run with --scan to get full insights from a Build Scan (powered by Develocity).

Deprecated Gradle features were used in this build, making it incompatible with Gradle 10.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/9.5.1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD FAILED in 2m 43s
43 actionable tasks: 21 executed, 1 from cache, 21 up-to-date


The JVM run finished with 3 failures. I’m reading the test result XML and the relevant screenshot test to identify exactly which snapshots need refreshing.


rg -n "<failure|FAILED|SetupScreenshotTest" app/build/test-results/testDebugUnitTest


sed -n '1,180p' app/src/test/java/com/yorvana/ui/navigation/SetupScreenshotTest.kt


rg -n "roborazzi|recordRoborazzi|verifyRoborazzi|test.record|test.verify|captureRoboImage|compareRoboImage" -S .