Output:
Chunk ID: 0d8e71
Wall time: 0.1238 seconds
Process exited with code 0
Original token count: 4632
Output:
# GMD regression tests on CI.
#
# This mirrors the smoke workflow's emulator setup but keeps PR execution
# opt-in through regression-label.yml. Nightly scheduled runs execute the
# Phase 1 full aggregate; label/reusable calls execute the core tier. Manual
# runs default to core but can opt into full via run_full.
name: Regression
permissions:
  contents: read

on:
  workflow_dispatch:
    inputs:
      run_full:
        description: 'Run full regression suite (regressionFullCheck) instead of core only'
        type: boolean
        default: false
  workflow_call:
  schedule:
    - cron: '17 8 * * *'

jobs:
  regression:
    name: GMD Regression Tests
    runs-on: ubuntu-latest
    timeout-minutes: 90
    outputs:
      skip_nightly: ${{ steps.check_commits.outputs.skip }}
    concurrency:
      group: regression-${{ github.ref }}
      cancel-in-progress: true
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          lfs: true

      - name: Check for recent commits
        id: check_commits
        if: github.event_name == 'schedule'
        run: |
          commit_time=$(git log -1 --invert-grep --grep='\[skip ci\]' --format=%ct)
          current_time=$(date +%s)
          diff=$((current_time - commit_time))
          # 24 hours (86400) + 5 min buffer
          if [ "$diff" -gt 86700 ]; then
            echo "No commits in the last 24 hours. Skipping nightly run."
            echo "skip=true" >> $GITHUB_OUTPUT
          else
            echo "New commits detected. Running nightly checks."
            echo "skip=false" >> $GITHUB_OUTPUT
          fi

      - name: Set up JDK 17
        if: github.event_name != 'schedule' || steps.check_commits.outputs.skip != 'true'
        uses: actions/setup-java@v5
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Set up Gradle
        if: github.event_name != 'schedule' || steps.check_commits.outputs.skip != 'true'
        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
        if: github.event_name != 'schedule' || steps.check_commits.outputs.skip != 'true'
        run: chmod +x gradlew

      - name: Enable KVM
        if: github.event_name != 'schedule' || steps.check_commits.outputs.skip != 'true'
        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 Regression Check
        if: github.event_name != 'schedule' && !(github.event_name == 'workflow_dispatch' && inputs.run_full)
        run: ./gradlew regressionCheck -Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirect

      - name: Run Nightly Regression Checks
        if: (github.event_name == 'schedule' && steps.check_commits.outputs.skip != 'true') || (github.event_name == 'workflow_dispatch' && inputs.run_full)
        run: ./gradlew regressionFullCheck -Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirect

      - name: Upload Managed Device Reports
        if: always() && (github.event_name != 'schedule' || steps.check_commits.outputs.skip != 'true')
        uses: actions/upload-artifact@v7
        with:
          name: gmd-regression-test-reports
          path: |
            app/build/reports/androidTests/managedDevice/
            app/build/outputs/androidTest-results/managedDevice/
            app/build/outputs/androidTest-results/regression-core-preserved/
            app/build/outputs/managed_device_android_test_additional_output/
            macrobenchmark/build/reports/androidTests/managedDevice/
            macrobenchmark/build/outputs/androidTest-results/managedDevice/
            macrobenchmark/build/outputs/managed_device_android_test_additional_output/
          if-no-files-found: warn
          retention-days: 7

  aggregate-results:
    name: Aggregate & Publish Regression Stats
    runs-on: ubuntu-latest
    needs: regression
    # Only aggregate/publish to the dashboard for nightly scheduled runs OR
    # manual runs that execute the full suite (since core-only runs do not
    # generate regression-core-preserved/, publishing would result in incomplete stats).
    if: >
      always() &&
      (needs.regression.result == 'success' || needs.regression.result == 'failure') &&
      needs.regression.outputs.skip_nightly != 'true' &&
      (
        github.event_name == 'schedule' ||
        (github.event_name == 'workflow_dispatch' && inputs.run_full)
      )
    permissions:
      contents: write
      pull-requests: write
    steps:
      - name: Checkout code
        uses: actions/checkout@v6
        with:
          ref: ${{ github.ref_name }}
          lfs: true

      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.x'

      - name: Download Managed Device Reports
        uses: actions/download-artifact@v4
        with:
          name: gmd-regression-test-reports
          path: tmp-reports
      - name: Aggregate and Push Results
        run: |
          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"
          mkdir -p .git/info
          echo "app/coverage-baselines/*.ec -filter -diff -merge" >> .git/info/attributes
          git reset --hard HEAD

          git fetch origin ${{ github.sha }}

          for i in {1..3}; do
            echo "Attempt $i to aggregate and push..."
            git pull --rebase origin ${{ github.ref_name }}
            git checkout ${{ github.sha }} -- tools/

            python3 tools/aggregate_regression.py
            git add docs/regression-history.json docs/regression-dashboard.md
            if git diff --quiet && git diff --staged --quiet; then
              echo "No changes to dashboard."
              if [ -f tmp-reports/infra_failed.txt ]; then
                echo "ERROR: Infrastructure failure detected during aggregation!"
                exit 1
              fi
              exit 0
            fi
            git commit -m "docs: update nightly regression dashboard [skip ci]"
            if git push origin ${{ github.ref_name }}; then
              echo "Successfully pushed dashboard update."
              if [ -f tmp-reports/infra_failed.txt ]; then
                echo "ERROR: Infrastructure failure detected during aggregation!"
                exit 1
              fi
              exit 0
            else
              echo "Push failed (concurrent commit). Retrying..."
              git reset --hard HEAD~1
            fi
          done
          echo "Failed to push dashboard update after 3 attempts."
          exit 1
    }

    buildFeatures {
        compose = true
        buildConfig = true
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
            excludes += "META-INF/LICENSE.md"
            excludes += "META-INF/LICENSE-notice.md"
        }
    }

    testOptions {
        unitTests {
            isIncludeAndroidResources = true
        }
        managedDevices {
            allDevices {
                create<com.android.build.api.dsl.ManagedVirtualDevice>("pixel2api33") {
                    device = "Pixel 2"
                    apiLevel = 33
                    systemImageSource = "aosp"
                    testedAbi = "x86_64"
                }
                create<com.android.build.api.dsl.ManagedVirtualDevice>("phonePixel6api34") {
                    device = "Pixel 6"
                    apiLevel = 34
                    systemImageSource = "aosp"
                    testedAbi = "x86_64"
                }
                create<com.android.build.api.dsl.ManagedVirtualDevice>("tablet7Nexus7api34") {
                    device = "Nexus 7"
                    apiLevel = 34
                    systemImageSource = "aosp"
                    testedAbi = "x86_64"
                }
                create<com.android.build.api.dsl.ManagedVirtualDevice>("tablet10PixelTabletApi34") {
                    device = "Pixel Tablet"
                    apiLevel = 34
                    systemImageSource = "aosp"
                    testedAbi = "x86_64"
                }
            }
        }
    }

    lint {
        abortOnError = true
        warningsAsErrors = false
        xmlReport = true
        htmlReport = true
        baseline = file("lint-baseline.xml")
    }
}

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
    compilerOptions {
        jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
    }
}

detekt {
    config.setFrom(rootProject.file("config/detekt/detekt.yml"))
    buildUponDefaultConfig = true
    autoCorrect = true
}

tasks.named("check") {
    dependsOn("lintDebug")
}

afterEvaluate {
    val requestedTaskNames = gradle.startParameter.taskNames.map { it.substringAfterLast(":") }
    val screenshotDeviceNames = setOf("phonePixel6api34", "tablet7Nexus7api34", "tablet10PixelTabletApi34")
    val isStoreScreenshotRun =
        requestedTaskNames.any { requestedTaskName ->
            requestedTaskName == "generateStoreScreenshots" || requestedTaskName.endsWith("StoreScreenshots")
        }
    val isSmokeCoverageRun = requestedTaskNames.any { it == "generateGmdCoverage" }

    // Note: This reflective block and the use of afterEvaluate make the build incompatible with
    // the Configuration Cache (CC) for managed device test tasks. ManagedDeviceInstrumentationTestTask
    // cannot be serialized when configured reflectively in afterEvaluate. We accept this trade-off
    // to enable per-device instrumentation arguments which AGP 8.x does not yet expose via a public DSL.
    tasks.withType<com.android.build.gradle.internal.tasks.ManagedDeviceInstrumentationTestTask>().configureEach {
        fun isTaskForDevice(
            taskName: String,
            deviceName: String,
        ): Boolean = taskName.equals("$deviceName${testVariantCap}AndroidTest", ignoreCase = true)

        fun isRequestedForDevice(deviceName: String): Boolean =
            requestedTaskNames.any { requestedTaskName ->
                requestedTaskName.equals("$deviceName${testVariantCap}AndroidTest", ignoreCase = true) ||
                    requestedTaskName.equals("${deviceName}Check", ignoreCase = true)
            } ||
                (deviceName == "pixel2api33" && isSmokeCoverageRun) ||
                (deviceName in screenshotDeviceNames && isStoreScreenshotRun)

        val deviceDirName =
            when {
                isTaskForDevice(name, "phonePixel6api34") -> "phonePixel6api34"
                isTaskForDevice(name, "tablet7Nexus7api34") -> "tablet7Nexus7api34"
                isTaskForDevice(name, "tablet10PixelTabletApi34") -> "tablet10PixelTabletApi34"
                isTaskForDevice(name, "pixel2api33") -> "pixel2api33"
                else -> null
            }
        if (deviceDirName != null) {
            // Set the host-side directory where GMD will pull files from.
            getAdditionalTestOutputDir().set(
                layout.buildDirectory.dir("outputs/managed_device_android_test_additional_output/$testVariant/$deviceDirName"),
            )
        }

        // AGP does not provide a public DSL to set per-ManagedVirtualDevice instrumentation arguments.
        // We use reflection to reach into the internal testData property and its instrumentationRunnerArguments map.
        val getTestDataMethod = javaClass.getMethod("getTestData")
        val testDataProperty = getTestDataMethod.invoke(this) as org.gradle.api.provider.Property<*>
        val testData = testDataProperty.get()
        val getArgsMethod = testData.javaClass.getMethod("getInstrumentationRunnerArguments")

        @Suppress("UNCHECKED_CAST")
        val argsProperty = getArgsMethod.invoke(testData) as org.gradle.api.provider.MapProperty<String, String>

        val myTask = this
        val taskName = name
        // Intentionally captured at configuration time: direct invocations and child GradleBuild
        // regression tasks each configure a fresh build with their own -P runner arguments.
        val isExplicitAnnotationRun = providers.gradleProperty("android.testInstrumentationRunnerArguments.annotation").isPresent
        argsProperty.putAll(
            provider {
                val map = mutableMapOf<String, String>()
                // ManagedDeviceInstrumentationTestTask instances can share AGP's underlying
                // runner argument map during configuration. Only the device task requested by
                // this Gradle invocation contributes arguments, while regression GradleBuild
                // children pass their explicit annotation filters in their own invocation.
                if (!isRequestedForDevice(deviceDirName.orEmpty())) {
                    return@provider map
                }
                when {
                    isTaskForDevice(taskName, "phonePixel6api34") -> {
                        map["package"] = screenshotTestPackage
                        map["additionalTestOutputDir"] = screenshotDeviceOutputDir
                    }
                    isTaskForDevice(taskName, "tablet7Nexus7api34") -> {
                        map["package"] = screenshotTestPackage
                        map["additionalTestOutputDir"] = screenshotDeviceOutputDir
                    }
                    isTaskForDevice(taskName, "tablet10PixelTabletApi34") -> {
                        map["package"] = screenshotTestPackage
                        map["additionalTestOutputDir"] = screenshotDeviceOutputDir
                    }
                    isTaskForDevice(taskName, "pixel2api33") -> {
                        // Keep this exact AOSP smoke task match. A future Play/staging billing
                        // task must not inherit the smoke annotation filter.
                        if (!isExplicitAnnotationRun) {
                            map["annotation"] = smokeAnnotation
                        }
                        map["notPackage"] = screenshotTestPackage
                        map["additionalTestOutputDir"] = flakesDeviceOutputDir
                    }
                }
                map
            },
        )
    }
}

fun registerRegressionDeviceTask(
    taskName: String,
    descriptionText: String,
    runnerArguments: Map<String, String>,
): TaskProvider<GradleBuild> =
    tasks.register<GradleBuild>(taskName) {
        group = "Verification"
        description = descriptionText
        dir = rootProject.projectDir
        buildName = taskName
        tasks = listOf(":app:$regressionDeviceTaskName")
        // Regression aggregate tasks own their runner filters; these values intentionally
        // override any outer -Pandroid.testInstrumentationRunnerArguments.annotation settings.
        startParameter.projectProperties.putAll(
            gradle.startParameter.projectProperties +
                runnerArguments.mapKeys { (key, _) -> "android.testInstrumentationRunnerArguments.$key" },
        )
    }

val regressionCheck =
    registerRegressionDeviceTask(
        taskName = "regressionCheck",
        descriptionText = "Runs core regression androidTests on the Pixel 2 API 33 managed device.",
        runnerArguments =
            mapOf(
                "annotation" to regressionAnnotation,
                "notAnnotation" to quarantinedAnnotation,
            ),
    )

val regressionFullCoreCheck =
    registerRegressionDeviceTask(
        taskName = "regressionFullCoreCheck",
        descriptionText = "Runs all core regression androidTests, including quarantined tests.",
        runnerArguments = mapOf("annotation" to regressionAnnotation),
    )

val regressionFullExtendedCheck =
    registerRegressionDeviceTask(
        taskName = "regressionFullExtendedCheck",
        descriptionText = "Runs full regression androidTests on the Pixel 2 API 33 managed device.",
        runnerArguments = mapOf("annotation" to regressionFullAnnotation),
    )

// Preserve @Regression XMLs written by regressionFullCoreCheck before
// regressionFullExtendedCheck overwrites the shared managedDevice/ output directory.
val preserveRegressionCoreResults =
    tasks.register<Copy>("preserveRegressionCoreResults") {
        group = "Verification"
        description = "Copies @Regression XML results to a stable directory so they survive the subsequent regressionFullExtendedCheck run."
        from(
            layout.buildDirectory.dir(
                "outputs/androidTest-results/managedDevice",
            ),
        )
        into(
            layout.buildDirectory.dir(
                "outputs/androidTest-results/regression-core-preserved",
            ),
        )
        duplicatesStrategy = DuplicatesStrategy.INCLUDE
        doFirst {
            project.delete(destinationDir)
        }
    }

regressionFullCoreCheck.configure { finalizedBy(preserveRegressionCoreResults) }
regressionFullExtendedCheck.configure { mustRunAfter(preserveRegressionCoreResults) }

tasks.register("regressionFullCheck") {
    group = "Verification"
    description = "Runs full regression checks, including the macrobenchmark sibling task."
    dependsOn(regressionFullCoreCheck, regressionFullExtendedCheck, macrobenchmarkTaskPath)
}

tasks.register<Copy>("generateGmdCoverage") {
    group = "Verification"
    description = "Runs GMD tests and copies the coverage file to the baseline directory."

    // Coverage is always collected from the debug variant of the smoke device
    // regardless of the current -PscreenshotBuild setting.
    val smokeTask = "pixel2api33DebugAndroidTest"
    dependsOn(smokeTask)

    from(layout.buildDirectory.dir("outputs/managed_device_code_coverage/debug/pixel2api33")) {
        include("coverage.ec")
    }
    into(layout.projectDirectory.dir("coverage-baselines"))
    rename { "gmd_smoke.ec" }

    includeEmptyDirs = false
    duplicatesStrategy = DuplicatesStrategy.FAIL

    doLast {
        val destFile = layout.projectDirectory.file("coverage-baselines/gmd_smoke.ec").asFile
        if (!destFile.exists()) {


Approved command prefix saved:
- ["gh", "run"]


Output:
Chunk ID: 3ce5d4
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 1049
Output:
{"conclusion":"failure","event":"schedule","headSha":"3443923e1a2a6ace919b44af3a3f749e317e6bac","jobs":[{"completedAt":"2026-06-15T15:04:34Z","conclusion":"failure","databaseId":81447065379,"name":"GMD Regression Tests","startedAt":"2026-06-15T14:35:08Z","status":"completed","steps":[{"completedAt":"2026-06-15T14:35:14Z","conclusion":"success","name":"Set up job","number":1,"startedAt":"2026-06-15T14:35:09Z","status":"completed"},{"completedAt":"2026-06-15T14:35:15Z","conclusion":"success","name":"Checkout code","number":2,"startedAt":"2026-06-15T14:35:14Z","status":"completed"},{"completedAt":"2026-06-15T14:35:15Z","conclusion":"success","name":"Check for recent commits","number":3,"startedAt":"2026-06-15T14:35:15Z","status":"completed"},{"completedAt":"2026-06-15T14:35:16Z","conclusion":"success","name":"Set up JDK 17","number":4,"startedAt":"2026-06-15T14:35:15Z","status":"completed"},{"completedAt":"2026-06-15T14:35:27Z","conclusion":"success","name":"Set up Gradle","number":5,"startedAt":"2026-06-15T14:35:16Z","status":"completed"},{"completedAt":"2026-06-15T14:35:28Z","conclusion":"success","name":"Grant execute permission for gradlew","number":6,"startedAt":"2026-06-15T14:35:27Z","status":"completed"},{"completedAt":"2026-06-15T14:35:28Z","conclusion":"success","name":"Enable KVM","number":7,"startedAt":"2026-06-15T14:35:28Z","status":"completed"},{"completedAt":"2026-06-15T14:35:28Z","conclusion":"skipped","name":"Run Regression Check","number":8,"startedAt":"2026-06-15T14:35:28Z","status":"completed"},{"completedAt":"2026-06-15T15:04:09Z","conclusion":"failure","name":"Run Nightly Regression Checks","number":9,"startedAt":"2026-06-15T14:35:28Z","status":"completed"},{"completedAt":"2026-06-15T15:04:11Z","conclusion":"success","name":"Upload Managed Device Reports","number":10,"startedAt":"2026-06-15T15:04:09Z","status":"completed"},{"completedAt":"2026-06-15T15:04:31Z","conclusion":"success","name":"Post Set up Gradle","number":18,"startedAt":"2026-06-15T15:04:11Z","status":"completed"},{"completedAt":"2026-06-15T15:04:32Z","conclusion":"success","name":"Post Set up JDK 17","number":19,"startedAt":"2026-06-15T15:04:31Z","status":"completed"},{"completedAt":"2026-06-15T15:04:32Z","conclusion":"success","name":"Post Checkout code","number":20,"startedAt":"2026-06-15T15:04:32Z","status":"completed"},{"completedAt":"2026-06-15T15:04:32Z","conclusion":"success","name":"Complete job","number":21,"startedAt":"2026-06-15T15:04:32Z","status":"completed"}],"url":"https://github.com/yorvana/android/actions/runs/27553797634/job/81447065379"},{"completedAt":"2026-06-15T15:04:47Z","conclusion":"success","databaseId":81453911597,"name":"Aggregate & Publish Regression Stats","startedAt":"2026-06-15T15:04:37Z","status":"completed","steps":[{"completedAt":"2026-06-15T15:04:39Z","conclusion":"success","name":"Set up job","number":1,"startedAt":"2026-06-15T15:04:38Z","status":"completed"},{"completedAt":"2026-06-15T15:04:41Z","conclusion":"success","name":"Checkout code","number":2,"startedAt":"2026-06-15T15:04:39Z","status":"completed"},{"completedAt":"2026-06-15T15:04:41Z","conclusion":"success","name":"Set up Python","number":3,"startedAt":"2026-06-15T15:04:41Z","status":"completed"},{"completedAt":"2026-06-15T15:04:42Z","conclusion":"success","name":"Download Managed Device Reports","number":4,"startedAt":"2026-06-15T15:04:41Z","status":"completed"},{"completedAt":"2026-06-15T15:04:45Z","conclusion":"success","name":"Aggregate and Push Results","number":5,"startedAt":"2026-06-15T15:04:42Z","status":"completed"},{"completedAt":"2026-06-15T15:04:45Z","conclusion":"success","name":"Post Set up Python","number":9,"startedAt":"2026-06-15T15:04:45Z","status":"completed"},{"completedAt":"2026-06-15T15:04:45Z","conclusion":"success","name":"Post Checkout code","number":10,"startedAt":"2026-06-15T15:04:45Z","status":"completed"},{"completedAt":"2026-06-15T15:04:46Z","conclusion":"success","name":"Complete job","number":11,"startedAt":"2026-06-15T15:04:46Z","status":"completed"}],"url":"https://github.com/yorvana/android/actions/runs/27553797634/job/81453911597"}],"url":"https://github.com/yorvana/android/actions/runs/27553797634"}


Allow GitHub API access to retrieve the failed regression job log?


Output:
Chunk ID: 81f150
Wall time: 1.0005 seconds
Process running with session ID 97324
Original token count: 0
Output:


Output:
Chunk ID: 04ddc4
Wall time: 0.0001 seconds
Process exited with code 0
Original token count: 17241
Output:
Total output lines: 412

GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6336917Z ##[group]Run ./gradlew regressionFullCheck -Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirect
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6338401Z ./gradlew regressionFullCheck -Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirect
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6379851Z shell: /usr/bin/bash -e {0}
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6380169Z env:
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6380560Z   JAVA_HOME: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.19-10/x64
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6381164Z   JAVA_HOME_17_X64: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.19-10/x64
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6381920Z   GRADLE_ACTION_ID: gradle/actions/setup-gradle
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6382312Z   GRADLE_USER_HOME: /home/runner/.gradle
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6382667Z   GRADLE_BUILD_ACTION_SETUP_COMPLETED: true
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6383186Z   DEVELOCITY_INJECTION_INIT_SCRIPT_NAME: gradle-actions.inject-develocity.init.gradle
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6383743Z   DEVELOCITY_INJECTION_CUSTOM_VALUE: gradle-actions
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6384150Z   GITHUB_DEPENDENCY_GRAPH_ENABLED: false
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:28.6384543Z ##[endgroup]
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:33.6633387Z Starting a Gradle Daemon (subsequent builds will be faster)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:44.3532626Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:44.3557510Z > Configure project :app
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:44.3582756Z WARNING: The option setting 'android.testoptions.manageddevices.emulator.gpu=swiftshader_indirect' is experimental.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:49.4669169Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:49.4712666Z > Task :macrobenchmark:preBuild UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:49.4879061Z > Task :macrobenchmark:preBenchmarkBuild UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:50.2796711Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:50.2797071Z > Configure project :regressionFullCoreCheck:app
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:50.2798275Z WARNING: The option setting 'android.testoptions.manageddevices.emulator.gpu=swiftshader_indirect' is experimental.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:50.5707985Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:50.5709032Z > Task :regressionFullCoreCheck:app:preBuild UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:50.5710688Z > Task :regressionFullCoreCheck:app:preDebugBuild UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:50.8735081Z > Task :regressionFullCoreCheck:app:generateDebugBuildConfig
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:50.9693668Z > Task :regressionFullCoreCheck:app:generateDebugResources FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:51.4107028Z > Task :regressionFullCoreCheck:app:packageDebugResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:52.9510833Z > Task :regressionFullCoreCheck:app:processDebugNavigationResources FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:53.0510563Z > Task :macrobenchmark:checkTestedAppObfuscationBenchmark
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:53.0514222Z > Task :macrobenchmark:generateBenchmarkResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:53.0515233Z > Task :macrobenchmark:packageBenchmarkResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:54.8513608Z > Task :regressionFullCoreCheck:app:parseDebugLocalResources FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:35:54.8522955Z > Task :regressionFullCoreCheck:app:generateDebugRFile FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:01.5573073Z > Task :macrobenchmark:processBenchmarkNavigationResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:01.5973262Z > Task :regressionFullCoreCheck:app:javaPreCompileDebug FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:01.8707460Z > Task :regressionFullCoreCheck:app:checkDebugAarMetadata
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:01.8732232Z > Task :regressionFullCoreCheck:app:mapDebugSourceSetPaths
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:01.8934573Z > Task :regressionFullCoreCheck:app:compileDebugNavigationResources FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:06.6512672Z > Task :macrobenchmark:parseBenchmarkLocalResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:07.2527450Z > Task :regressionFullCoreCheck:app:mergeDebugResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:07.2567360Z > Task :regressionFullCoreCheck:app:createDebugCompatibleScreenManifests
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:07.2586044Z > Task :regressionFullCoreCheck:app:extractDeepLinksDebug FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:07.9516685Z > Task :regressionFullCoreCheck:app:processDebugMainManifest
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:08.1512537Z > Task :regressionFullCoreCheck:app:processDebugManifest
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:08.2525815Z > Task :macrobenchmark:generateBenchmarkRFile
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:11.5556535Z > Task :regressionFullCoreCheck:app:processDebugManifestForPackage
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:18.1017857Z > Task :macrobenchmark:compileBenchmarkKotlin
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:18.2071000Z > Task :macrobenchmark:javaPreCompileBenchmark FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:18.2643469Z > Task :macrobenchmark:compileBenchmarkJavaWithJavac NO-SOURCE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:18.2662268Z > Task :macrobenchmark:generateBenchmarkAssets UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:19.4543110Z > Task :app:regressionFullCoreCheck
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:19.5529024Z > Task :regressionFullCoreCheck:app:processDebugResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:19.5626703Z > Task :regressionFullCoreCheck:app:preDebugAndroidTestBuild SKIPPED
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:19.9522704Z > Task :macrobenchmark:mergeBenchmarkAssets
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:24.8553874Z > Task :regressionFullCoreCheck:app:compileDebugKotlin
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:26.3519518Z > Task :regressionFullCoreCheck:app:processDebugAndroidTestManifest
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:26.4532888Z > Task :regressionFullCoreCheck:app:generateDebugAndroidTestBuildConfig
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:26.4562249Z > Task :regressionFullCoreCheck:app:generateDebugAndroidTestResources FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:26.4582296Z > Task :regressionFullCoreCheck:app:packageDebugAndroidTestResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:26.4622397Z > Task :regressionFullCoreCheck:app:processDebugAndroidTestNavigationResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:26.4643364Z > Task :regressionFullCoreCheck:app:parseDebugAndroidTestLocalResources FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:28.8510228Z > Task :macrobenchmark:compressBenchmarkAssets
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:28.8534173Z > Task :macrobenchmark:desugarBenchmarkFileDependencies
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:29.0520509Z > Task :macrobenchmark:checkBenchmarkAarMetadata
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:29.0521391Z > Task :regressionFullCoreCheck:app:generateDebugAndroidTestRFile
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:29.0522474Z > Task :regressionFullCoreCheck:app:javaPreCompileDebugAndroidTest FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:29.0523291Z > Task :regressionFullCoreCheck:app:generateDebugAssets UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:29.0523990Z > Task :macrobenchmark:mapBenchmarkSourceSetPaths
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:29.1577106Z > Task :macrobenchmark:compileBenchmarkNavigationResources FROM-CACHE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:29.7642655Z > Task :regressionFullCoreCheck:app:mergeDebugAssets
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:29.8977454Z > Task :regressionFullCoreCheck:app:compressDebugAssets
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:30.1013956Z > Task :macrobenchmark:mergeBenchmarkResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:30.3996735Z > Task :macrobenchmark:processBenchmarkManifest
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:37.3513062Z > Task :regressionFullCoreCheck:app:generateDebugGlobalSynthetics
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:38.6043818Z > Task :macrobenchmark:processBenchmarkResources
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:38.7043002Z > Task :regressionFullCoreCheck:app:checkDebugDuplicateClasses
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:36:38.7089768Z > Task :regressionFullCoreCheck:app:desugarDebugFileDependencies
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:08.5532630Z > Task :macrobenchmark:dexBuilderBenchmark
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:08.6510334Z > Task :regressionFullCoreCheck:app:mergeExtDexDebug
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:18.7942918Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:18.7962599Z > Task :regressionFullCoreCheck:app:compileDebugKotlin
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:18.8365937Z w: file:///home/runner/work/android/android/app/src/main/java/com/yorvana/ui/records/ImageViewerScreen.kt:62: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.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:18.8745922Z w: file:///home/runner/work/android/android/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.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:18.8752121Z w: file:///home/runner/work/android/android/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:185: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.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:18.9164557Z w: file:///home/runner/work/android/android/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:414:49 'val Icons.Filled.InsertDriveFile: ImageVector' is deprecated. Use the AutoMirrored version at Icons.AutoMirrored.Filled.InsertDriveFile.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:18.9204485Z w: file:///home/runner/work/android/android/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:486: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.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:18.9576360Z w: file:///home/runner/work/android/android/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:493: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.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:18.9585200Z w: file:///home/runner/work/android/android/app/src/main/java/com/yorvana/ui/records/RecordDetailScreen.kt:584:107 'val Icons.Filled.InsertDriveFile: ImageVector' is deprecated. Use the AutoMirrored version at Icons.AutoMirrored.Filled.InsertDriveFile.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:19.0004206Z w: file:///home/runner/work/android/android/app/src/main/java/com/yorvana/util/SentryWrapper.kt:5:8 'class UserFeedback : Any, JsonUnknown, JsonSerializable' is deprecated. Deprecated in Java.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:19.0034504Z w: file:///home/runner/work/android/android/app/src/main/java/com/yorvana/util/SentryWrapper.kt:56:17 'constructor(p0: SentryId!): UserFeedback' is deprecated. Deprecated in Java.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:19.0414199Z w: file:///home/runner/work/android/android/app/src/main/java/com/yorvana/util/SentryWrapper.kt:60:20 'static fun captureUserFeedback(p0: UserFeedback): Unit' is deprecated. Deprecated in Java.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:50.9510078Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:37:50.9532767Z > Task :macrobenchmark:mergeBenchmarkGlobalSynthetics
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:14.4510795Z > Task :macrobenchmark:processBenchmarkJavaRes
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:15.7521053Z > Task :macrobenchmark:mergeBenchmarkJavaResource
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:16.0512613Z > Task :macrobenchmark:checkBenchmarkDuplicateClasses
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:20.9522750Z > Task :macrobenchmark:mergeExtDexBenchmark
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:25.3542332Z > Task :macrobenchmark:mergeDexBenchmark
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:25.3543185Z > Task :macrobenchmark:mergeBenchmarkJniLibFolders
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:25.6532631Z > Task :macrobenchmark:mergeBenchmarkNativeLibs
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:27.0512783Z > Task :macrobenchmark:stripBenchmarkDebugSymbols
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:28.9509525Z > Task :macrobenchmark:validateSigningBenchmark
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:29.0532716Z > Task :macrobenchmark:writeBenchmarkSigningConfigVersions
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:30.5525799Z > Task :macrobenchmark:packageBenchmark
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:30.6527817Z > Task :macrobenchmark:createBenchmarkApkListingFileRedirect
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:32.6522011Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:32.6542629Z > Task :macrobenchmark:pixel2api33Setup
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:32.6546290Z The device "pixel2api33" does not specify a "testedAbi".
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:32.6572411Z This currently defaults to "x86_64", but will change to "arm64-v8a" in AGP 10.0.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:32.6591928Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:32.6592684Z The system image configured for "pixel2api33" does not support NDK translation,
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:32.6612327Z so it will be unable to run tests built for "arm64-v8a".
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:32.6632032Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:32.6652440Z To keep the current behavior and prevent test failures in AGP 10.0,
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:32.6653458Z explicitly set the ABI: testedAbi = "x86_64"
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:34.0532904Z Checking the license for package Android Emulator in /usr/local/lib/android/sdk/licenses
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:34.0534324Z License for package Android Emulator accepted.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:34.0552291Z Preparing "Install Android Emulator v.36.6.11".
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:50.6515074Z "Install Android Emulator v.36.6.11" ready.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:50.6532129Z Installing Android Emulator in /usr/local/lib/android/sdk/emulator
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:50.6544107Z "Install Android Emulator v.36.6.11" complete.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:51.2510263Z "Install Android Emulator v.36.6.11" finished.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:51.2572481Z Checking the license for package Intel x86_64 Atom System Image in /usr/local/lib/android/sdk/licenses
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:51.2612186Z License for package Intel x86_64 Atom System Image accepted.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:38:51.2632167Z Preparing "Install Intel x86_64 Atom System Image API 33 (revision 2)".
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:39:27.7511114Z "Install Intel x86_64 Atom System Image API 33 (revision 2)" …7241 tokens truncated…on Checks	2026-06-15T14:50:37.9583430Z > Task :regressionFullExtendedCheck:app:mergeLibDexDebug UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9588627Z > Task :regressionFullExtendedCheck:app:dexBuilderDebug UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9606199Z > Task :regressionFullExtendedCheck:app:mergeProjectDexDebug UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9632861Z > Task :regressionFullExtendedCheck:app:mergeDebugJniLibFolders UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9643713Z > Task :regressionFullExtendedCheck:app:mergeDebugNativeLibs UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9645092Z > Task :regressionFullExtendedCheck:app:stripDebugDebugSymbols UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9649057Z > Task :regressionFullExtendedCheck:app:validateSigningDebug UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9650186Z > Task :regressionFullExtendedCheck:app:writeDebugAppMetadata UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9672532Z > Task :regressionFullExtendedCheck:app:writeDebugSigningConfigVersions UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9684575Z > Task :regressionFullExtendedCheck:app:packageDebug UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9686831Z > Task :regressionFullExtendedCheck:app:createDebugApkListingFileRedirect UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9688849Z > Task :regressionFullExtendedCheck:app:generateDebugAndroidTestAssets UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9691707Z > Task :regressionFullExtendedCheck:app:mergeDebugAndroidTestAssets UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:37.9695142Z > Task :regressionFullExtendedCheck:app:compressDebugAndroidTestAssets UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0516051Z > Task :regressionFullExtendedCheck:app:generateDebugAndroidTestGlobalSynthetics UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0554111Z > Task :regressionFullExtendedCheck:app:processDebugAndroidTestJavaRes UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0572976Z > Task :regressionFullExtendedCheck:app:mergeDebugAndroidTestJavaResource UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0583703Z > Task :regressionFullExtendedCheck:app:checkDebugAndroidTestDuplicateClasses UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0605928Z > Task :regressionFullExtendedCheck:app:desugarDebugAndroidTestFileDependencies UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0635227Z > Task :regressionFullExtendedCheck:app:mergeExtDexDebugAndroidTest UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0643738Z > Task :regressionFullExtendedCheck:app:mergeLibDexDebugAndroidTest UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0662621Z > Task :regressionFullExtendedCheck:app:checkDebugAndroidTestAarMetadata UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0667418Z > Task :regressionFullExtendedCheck:app:mapDebugAndroidTestSourceSetPaths UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0686211Z > Task :regressionFullExtendedCheck:app:compileDebugAndroidTestNavigationResources UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0722867Z > Task :regressionFullExtendedCheck:app:mergeDebugAndroidTestResources UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0728120Z > Task :regressionFullExtendedCheck:app:processDebugAndroidTestResources UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0729602Z > Task :regressionFullExtendedCheck:app:dexBuilderDebugAndroidTest UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0730880Z > Task :regressionFullExtendedCheck:app:mergeProjectDexDebugAndroidTest UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.0732502Z > Task :regressionFullExtendedCheck:app:mergeDebugAndroidTestJniLibFolders UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.1511687Z > Task :regressionFullExtendedCheck:app:mergeDebugAndroidTestNativeLibs UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.1524697Z > Task :regressionFullExtendedCheck:app:stripDebugAndroidTestDebugSymbols UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.1537983Z > Task :regressionFullExtendedCheck:app:validateSigningDebugAndroidTest UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.1539691Z > Task :regressionFullExtendedCheck:app:writeDebugAndroidTestSigningConfigVersions UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.1541738Z > Task :regressionFullExtendedCheck:app:packageDebugAndroidTest UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:38.1543860Z > Task :regressionFullExtendedCheck:app:createDebugAndroidTestApkListingFileRedirect UP-TO-DATE
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:39.4515574Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:39.4517340Z > Task :regressionFullExtendedCheck:app:pixel2api33Setup
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:39.4534263Z The device "pixel2api33" does not specify a "testedAbi".
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:39.4538345Z This currently defaults to "x86_64", but will change to "arm64-v8a" in AGP 10.0.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:39.4541035Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:39.4542068Z The system image configured for "pixel2api33" does not support NDK translation,
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:39.4547152Z so it will be unable to run tests built for "arm64-v8a".
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:39.4549579Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:39.4550179Z To keep the current behavior and prevent test failures in AGP 10.0,
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:50:39.4552165Z explicitly set the ABI: testedAbi = "x86_64"
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:51:05.4516022Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:51:05.4539905Z > Task :app:regressionFullExtendedCheck
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:51:09.4523050Z > Task :regressionFullExtendedCheck:app:pixel2api33DebugAndroidTest
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:51:21.9509470Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:51:21.9525284Z Starting 15 tests on pixel2api33
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:51:32.1009901Z pixel2api33 Tests 0/15 completed. (0 skipped) (0 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:51:41.9512781Z pixel2api33 Tests 1/15 completed. (0 skipped) (0 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:51:42.2514380Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:51:42.2622980Z com.yorvana.regression.attachments.AttachmentRealOsRegressionTest > rA04_nonImageAttachment_launchesSystemViewerIntent[pixel2api33] SKIPPED
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:51:51.9522654Z pixel2api33 Tests 3/15 completed. (1 skipped) (0 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:53:26.1522956Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:53:26.1563800Z com.yorvana.regression.attachments.AttachmentRealOsRegressionTest > rA06_attachmentStillResolvableAfterVaultMoveMidSession[pixel2api33] SKIPPED
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:53:32.0526915Z pixel2api33 Tests 5/15 completed. (2 skipped) (0 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:56:15.7532186Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:56:15.7541057Z com.yorvana.regression.attachments.AttachmentRealOsRegressionTest > rA07_cancelledCameraCaptureShowsErrorLeavesNoPartialAndRetrySucceeds[pixel2api33] FAILED
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:56:15.7544843Z 	java.lang.AssertionError: Failed to inject touch input.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:56:15.7546480Z 	Reason: Expected exactly '1' node but could not find any node that satisfies: (TestTag = 'RecordItem_851e1aa4-af86-4697-8c68-083f77e5e03c')
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:56:22.1053387Z pixel2api33 Tests 6/15 completed. (2 skipped) (1 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:57:38.3507862Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:57:38.3582209Z com.yorvana.regression.attachments.AttachmentRealOsRegressionTest > rA01_realSystemFilePicker_pdfPersistsAndViewIntentLaunches[pixel2api33] SKIPPED
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:57:42.0571928Z pixel2api33 Tests 8/15 completed. (3 skipped) (1 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:58:42.0536903Z pixel2api33 Tests 9/15 completed. (3 skipped) (1 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T14:58:52.0508840Z pixel2api33 Tests 12/15 completed. (3 skipped) (1 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:00:32.0510021Z pixel2api33 Tests 13/15 completed. (3 skipped) (1 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:00:42.0532013Z pixel2api33 Tests 14/15 completed. (3 skipped) (1 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:02:32.0509134Z pixel2api33 Tests 16/15 completed. (3 skipped) (1 failed)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.0508867Z Tests on pixel2api33 failed: There was 1 failure(s).
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.0542685Z Finished 18 tests on pixel2api33
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1553337Z Execute com.yorvana.regression.attachments.AttachmentRealOsRegressionTest.rA07_cancelledCameraCaptureShowsErrorLeavesNoPartialAndRetrySucceeds: FAILED
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1555368Z java.lang.AssertionError: java.lang.AssertionError: Failed to inject touch input.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1584193Z Reason: Expected exactly '1' node but could not find any node that satisfies: (TestTag = 'RecordItem_851e1aa4-af86-4697-8c68-083f77e5e03c')
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1620326Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1631713Z at androidx.compose.ui.test.SemanticsNodeInteraction.fetchOneOrThrow(SemanticsNodeInteraction.kt:178)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1747550Z at androidx.compose.ui.test.SemanticsNodeInteraction.fetchOneOrThrow$default(SemanticsNodeInteraction.kt:150)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1749318Z at androidx.compose.ui.test.SemanticsNodeInteraction.fetchSemanticsNode(SemanticsNodeInteraction.kt:84)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1750459Z at androidx.compose.ui.test.ActionsKt.performTouchInput(Actions.kt:401)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1751696Z at androidx.compose.ui.test.AndroidActions.performClickImpl(Actions.android.kt:23)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1752798Z at androidx.compose.ui.test.ActionsKt.performClick(Actions.kt:59)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1754200Z at com.yorvana.regression.attachments.AttachmentRealOsRegressionTest.openRecord(AttachmentRealOsRegressionTest.kt:345)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1756367Z at com.yorvana.regression.attachments.AttachmentRealOsRegressionTest.rA07_cancelledCameraCaptureShowsErrorLeavesNoPartialAndRetrySucceeds(AttachmentRealOsRegressionTest.kt:328)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1758344Z Suppressed: java.lang.IllegalStateException: Only a single call to `runTest` can be performed during one test.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1759746Z Suppressed: java.lang.IllegalStateException: Only a single call to `runTest` can be performed during one test.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1760523Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1760809Z java.lang.AssertionError: Failed to inject touch input.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1772393Z Reason: Expected exactly '1' node but could not find any node that satisfies: (TestTag = 'RecordItem_851e1aa4-af86-4697-8c68-083f77e5e03c')
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1773346Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1774007Z at androidx.compose.ui.test.SemanticsNodeInteraction.fetchOneOrThrow(SemanticsNodeInteraction.kt:178)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1775559Z at androidx.compose.ui.test.SemanticsNodeInteraction.fetchOneOrThrow$default(SemanticsNodeInteraction.kt:150)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1777085Z at androidx.compose.ui.test.SemanticsNodeInteraction.fetchSemanticsNode(SemanticsNodeInteraction.kt:84)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1778254Z at androidx.compose.ui.test.ActionsKt.performTouchInput(Actions.kt:401)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1779276Z at androidx.compose.ui.test.AndroidActions.performClickImpl(Actions.android.kt:23)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1780287Z at androidx.compose.ui.test.ActionsKt.performClick(Actions.kt:59)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1781706Z at com.yorvana.regression.attachments.AttachmentRealOsRegressionTest.openRecord(AttachmentRealOsRegressionTest.kt:345)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1783748Z at com.yorvana.regression.attachments.AttachmentRealOsRegressionTest.rA07_cancelledCameraCaptureShowsErrorLeavesNoPartialAndRetrySucceeds(AttachmentRealOsRegressionTest.kt:328)
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1785639Z Suppressed: java.lang.IllegalStateException: Only a single call to `runTest` can be performed during one test.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.1787642Z Suppressed: java.lang.IllegalStateException: Only a single call to `runTest` can be performed during one test.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.3529997Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.3543336Z > Task :regressionFullExtendedCheck:app:pixel2api33DebugAndroidTest FAILED
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.3554301Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.3555301Z > Task :regressionFullExtendedCheck:app:mergeDebugAndroidTestTestResultProtos
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.3558418Z Test execution completed. See the report at: file:///home/runner/work/android/android/app/build/reports/androidTests/managedDevice/debug/allDevices/index.html
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.4508076Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.4527807Z > Task :app:regressionFullExtendedCheck FAILED
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.5537370Z gradle/actions: Writing build results to /home/runner/work/_temp/.gradle-actions/build-results/__run_4-1781534138064.json
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6508192Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6522194Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6523429Z [Incubating] Problems report is available at: file:///home/runner/work/android/android/build/reports/problems/problems-report.html
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6583939Z FAILURE: Build failed with an exception.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6584596Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6584685Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6585358Z Deprecated Gradle features were used in this build, making it incompatible with Gradle 10.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6626261Z * What went wrong:
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6626606Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6652925Z You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6682871Z Execution failed for task ':regressionFullExtendedCheck:app:pixel2api33DebugAndroidTest' (registered by plugin 'com.android.internal.application').
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6683992Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6722933Z > There were failing tests. See the report at: file:///home/runner/work/android/android/app/build/reports/androidTests/managedDevice/debug/pixel2api33/index.html
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6732790Z 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.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6734604Z 35 actionable tasks: 33 executed, 2 from cache
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6792176Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6794288Z * Try:
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6794818Z > Run with --stacktrace option to get the stack trace.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6795477Z > Run with --info or --debug option to get more log output.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6796199Z > Run with --scan to get full insights from a Build Scan (powered by Develocity).
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6796917Z > Get more help at https://help.gradle.org.
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6797225Z
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:08.6797376Z BUILD FAILED in 28m 39s
GMD Regression Tests	Run Nightly Regression Checks	2026-06-15T15:04:09.7109380Z ##[error]Process completed with exit code 1.


The failure is isolated to `rA07_cancelledCameraCaptureShowsErrorLeavesNoPartialAndRetrySucceeds`: after the camera-cancel path, the test immediately clicks a record row that is absent from the Compose tree. The preceding commit only changed launcher icons/version metadata, so I’m treating this as either a test synchronization flaw or a genuine relaunch/navigation regression and will reproduce the single method locally with KVM plus `gpu=host`.


nl -ba app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt | sed -n '250,380p'


rg -n "rA07|cancelledCamera|openRecord\(|RecordItem_|camera" app/src/androidTest/java/com/yorvana -g '*.kt' docs/regression-history.json docs/regression-dashboard.md


git show 333b38a:docs/regression-dashboard.md | sed -n '1,180p'; git show 3443923:docs/regression-dashboard.md | sed -n '1,180p'


rm -rf /tmp/regression-run-27553797634 && mkdir -p /tmp/regression-run-27553797634 && gh run download 27553797634 -n gmd-regression-test-reports -D /tmp/regression-run-27553797634 && find /tmp/regression-run-27553797634 -maxdepth 6 -type f | sort | head -100

Allow downloading the failed regression artifacts so I can inspect reports and device output?


Output:
Chunk ID: 7a7930
Wall time: 0.1700 seconds
Process exited with code 0
Original token count: 1468
Output:
   250
   251	        composeRule.onNodeWithTag(TestTags.SETTINGS_BUTTON).performClick()
   252	        composeRule.awaitText(composeRule.activity.getString(R.string.settings_title), timeoutMillis = 60_000)
   253	        composeRule.onNodeWithTag(TestTags.SETTINGS_VAULT_CHANGE_BUTTON).performClick()
   254	        SystemPicker.createAndPickFolder(device, "Yorvana R-A06 ${System.currentTimeMillis()}")
   255	        composeRule.awaitText(composeRule.activity.getString(R.string.settings_vault_move_title), timeoutMillis = 60_000)
   256	        composeRule.onNodeWithTag(TestTags.SETTINGS_VAULT_MOVE_ACTION).performClick()
   257	        composeRule.awaitText(
   258	            composeRule.activity.getString(R.string.settings_vault_changed_success),
   259	            timeoutMillis = 120_000,
   260	            substring = true,
   261	        )
   262	        composeRule.onNodeWithText(composeRule.activity.getString(R.string.action_ok)).performClick()
   263	        composeRule.waitForIdle()
   264
   265	        // Navigate back from settings to vehicle list
   266	        composeRule.onNodeWithContentDescription(composeRule.activity.getString(R.string.action_back)).performClick()
   267	        composeRule.waitForIdle()
   268
   269	        openVehicle("Move Attachment Car")
   270	        openRecord(record)
   271	        assertExternalViewIntentLaunches("application/pdf") {
   272	            composeRule.onNodeWithTag(TestTags.recordDetailAttachmentThumbnail("after-move.pdf")).performClick()
   273	        }
   274	    }
   275
   276	    @Test
   277	    fun rA07_cancelledCameraCaptureShowsErrorLeavesNoPartialAndRetrySucceeds() {
   278	        val vehicle = composeRule.seedVehicle(nickname = "Camera Cancel Car")
   279
   280	        openAddRecord("Camera Cancel Car")
   281	        fillMinimalRecord("32345", "Camera Shop")
   282	        composeRule.onNodeWithTag(TestTags.ATTACHMENT_TAKE_PHOTO_BUTTON).performScrollTo().performClick()
   283	        Camera.captureAndCancel(device)
   284	        composeRule.awaitText("Photo capture was cancelled", substring = true, timeoutMillis = 20_000)
   285	        assertThat(
   286	            VaultFixture
   287	                .active()
   288	                .snapshot()
   289	                .keys
   290	                .none { it.endsWith(".jpg") },
   291	        ).isTrue()
   292
   293	        composeRule.onNodeWithTag(TestTags.ATTACHMENT_TAKE_PHOTO_BUTTON).performScrollTo().performClick()
   294	        Camera.captureAndAccept(device)
   295
   296	        // Wait for the attachment to appear in the UI list
   297	        val endTime = System.currentTimeMillis() + 30_000
   298	        var found = false
   299	        while (System.currentTimeMillis() < endTime) {
   300	            try {
   301	                if (composeRule.onAllNodesWithTag(TestTags.ATTACHMENT_ITEM).fetchSemanticsNodes().isNotEmpty()) {
   302	                    found = true
   303	                    break
   304	                }
   305	            } catch (_: Exception) {
   306	            }
   307	            Thread.sleep(500)
   308	        }
   309	        if (!found) error("Timeout waiting for attachment item to appear")
   310
   311	        composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
   312	        composeRule.waitForIdle()
   313	        val recordId =
   314	            runBlocking {
   315	                composeRule
   316	                    .app()
   317	                    .vaultStorage
   318	                    .listRecordIds(vehicle.id)
   319	                    .getOrThrow()
   320	                    .single()
   321	            }
   322	        val record =
   323	            runBlocking {
   324	                // Ensure the record is actually visible to the repository
   325	                composeRule.app().recordRepository.getRecord(vehicle.id, recordId)
   326	            }
   327	        assertThat(record).isNotNull()
   328	        openRecord(record!!)
   329	        assertThat(record.attachments.singleOrNull()).endsWith(".jpg")
   330	    }
   331
   332	    private fun openVehicle(name: String) {
   333	        composeRule.awaitText(name, timeoutMillis = 60_000)
   334	        composeRule.onNodeWithText(name).performClick()
   335	        composeRule.awaitTagVisible(TestTags.ADD_RECORD_FAB, timeoutMillis = 60_000)
   336	    }
   337
   338	    private fun openAddRecord(vehicleName: String) {
   339	        openVehicle(vehicleName)
   340	        composeRule.onNodeWithTag(TestTags.ADD_RECORD_FAB).performClick()
   341	        composeRule.awaitTagVisible(TestTags.RECORD_ODOMETER_FIELD, timeoutMillis = 60_000)
   342	    }
   343
   344	    private fun openRecord(record: com.yorvana.data.model.ServiceRecord) {
   345	        composeRule.onNodeWithTag(TestTags.recordItem(record.id)).performClick()
   346	        composeRule.awaitTagVisible(TestTags.RECORD_DETAIL_ODOMETER_TEXT, timeoutMillis = 60_000)
   347	    }
   348
   349	    private fun fillMinimalRecord(
   350	        odometer: String,
   351	        performer: String,
   352	    ) {
   353	        composeRule.onNodeWithTag(TestTags.RECORD_ODOMETER_FIELD).performTextInput(odometer)
   354	        composeRule.onNodeWithTag(TestTags.PERFORMER_FIELD).performTextInput(performer)
   355	    }
   356
   357	    private fun assertExternalViewIntentLaunches(
   358	        mimeType: String,
   359	        block: () -> Unit,
   360	    ) {
   361	        val intent =
   362	            Intent(Intent.ACTION_VIEW).apply {
   363	                type = mimeType
   364	            }
   365	        assumeTrue("No external viewer resolves $mimeType", ExternalViewer.hasResolver(composeRule.activity, intent))
   366	        block()
   367	        ExternalViewer.assertLaunchOrSkipUi(composeRule.activity, intent)
   368	    }
   369	}


Output:
Chunk ID: 3788bf
Wall time: 0.2000 seconds
Process exited with code 0
Original token count: 424
Output:
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:85:        openRecord(record)
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:145:        openRecord(record)
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:208:        openRecord(record!!)
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:219:        openRecord(record)
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:234:        openRecord(record)
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:270:        openRecord(record)
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:277:    fun rA07_cancelledCameraCaptureShowsErrorLeavesNoPartialAndRetrySucceeds() {
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:328:        openRecord(record!!)
app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt:344:    private fun openRecord(record: com.yorvana.data.model.ServiceRecord) {
app/src/androidTest/java/com/yorvana/SmokeTest.kt:46: * including filesystem access (SAF), camera integration, and the paywall.
app/src/androidTest/java/com/yorvana/testsupport/Camera.kt:15:            device.wait(Until.findObject(By.res("com.android.camera2:id/shutter_button")), TIMEOUT_MILLIS)
app/src/androidTest/java/com/yorvana/testsupport/Camera.kt:22:            device.wait(Until.findObject(By.res("com.android.camera2:id/done_button")), TIMEOUT_MILLIS)


Output:
Chunk ID: d04a75
Wall time: 0.1905 seconds
Process exited with code 0
Original token count: 2020
Output:
# Regression Test Dashboard & Flake Audit

Generated on: **2026-06-01 14:34:06 UTC**

Total historical runs tracked: **6** | Non-infra runs: **5** | Infra noise failures: **1**

## Suite Health Summary
| Metric | Value | Description |
|---|---|---|
| **Overall Pass Rate** | 100.00% | Evaluated tests passing or flaking successfully |
| **Suite Flake Rate** | 0.00% | Tests requiring 1 or more retries (Budget: ≤1.0%) |
| **Infra Noise Runs** | 1 | Build runs failing due to runner/emulator infrastructure |

## Quarantined Tests
> [!WARNING]
> Quarantined tests are excluded from gating PR builds to protect developer velocity, but continue to run in nightly regression runs.

| Scenario ID | Test Name / File | Tier | Since | Issue | Reason | Pass Rate (Last 100) | Streak | Eligible for Promotion |
|---|---|---|---|---|---|---|---|---|
| - | No tests currently quarantined | - | - | - | - | - | - | - |

## Active Regression Tests
| Scenario ID | Tier | Pass Rate | Flake Rate | Consecutive Passes | Status |
|---|---|---|---|---|---|
| **M-01** | `Regression` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **M-02** | `Regression` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **M-03** | `Regression` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **M-04** | `Regression` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-A01** | `RegressionFull` | N/A | N/A | 0 | 🟢 Healthy |
| **R-A02** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-A03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-A04** | `RegressionFull` | N/A | N/A | 0 | 🟢 Healthy |
| **R-A05** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-A06** | `RegressionFull` | N/A | N/A | 0 | 🟢 Healthy |
| **R-A07** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-C01** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-C02** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-C03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-D01** | `Regression` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-D02** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-D03** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-D04** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-L01** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-L02** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-L03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-L04** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-L05** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-L06** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-L07** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-P03** | `BillingSandbox` | N/A | N/A | 0 | 🟢 Healthy |
| **R-P04** | `BillingSandbox` | N/A | N/A | 0 | 🟢 Healthy |
| **R-P05** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-R01** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-R02** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-R03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-R04** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-S01** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-S02a** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-S02b** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-S02c** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-S03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-S04** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V01** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V02** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V04** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V05** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V06** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V07** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **scaffoldIsDiscoverable** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |# Regression Test Dashboard & Flake Audit

Generated on: **2026-06-01 14:34:06 UTC**

Total historical runs tracked: **6** | Non-infra runs: **5** | Infra noise failures: **1**

## Suite Health Summary
| Metric | Value | Description |
|---|---|---|
| **Overall Pass Rate** | 100.00% | Evaluated tests passing or flaking successfully |
| **Suite Flake Rate** | 0.00% | Tests requiring 1 or more retries (Budget: ≤1.0%) |
| **Infra Noise Runs** | 1 | Build runs failing due to runner/emulator infrastructure |

## Quarantined Tests
> [!WARNING]
> Quarantined tests are excluded from gating PR builds to protect developer velocity, but continue to run in nightly regression runs.

| Scenario ID | Test Name / File | Tier | Since | Issue | Reason | Pass Rate (Last 100) | Streak | Eligible for Promotion |
|---|---|---|---|---|---|---|---|---|
| - | No tests currently quarantined | - | - | - | - | - | - | - |

## Active Regression Tests
| Scenario ID | Tier | Pass Rate | Flake Rate | Consecutive Passes | Status |
|---|---|---|---|---|---|
| **M-01** | `Regression` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **M-02** | `Regression` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **M-03** | `Regression` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **M-04** | `Regression` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-A01** | `RegressionFull` | N/A | N/A | 0 | 🟢 Healthy |
| **R-A02** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-A03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-A04** | `RegressionFull` | N/A | N/A | 0 | 🟢 Healthy |
| **R-A05** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-A06** | `RegressionFull` | N/A | N/A | 0 | 🟢 Healthy |
| **R-A07** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-C01** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-C02** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-C03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-D01** | `Regression` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-D02** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-D03** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-D04** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-L01** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-L02** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-L03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-L04** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-L05** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-L06** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-L07** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-P03** | `BillingSandbox` | N/A | N/A | 0 | 🟢 Healthy |
| **R-P04** | `BillingSandbox` | N/A | N/A | 0 | 🟢 Healthy |
| **R-P05** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-R01** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-R02** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-R03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-R04** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-S01** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-S02a** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-S02b** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-S02c** | `RegressionFull` | 100.0% | 0.0% | 3 | 🟢 Healthy |
| **R-S03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-S04** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V01** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V02** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V03** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V04** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V05** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V06** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **R-V07** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |
| **scaffoldIsDiscoverable** | `Regression` | 100.0% | 0.0% | 5 | 🟢 Healthy |


Output:
Chunk ID: cf9182
Wall time: 1.0018 seconds
Process running with session ID 9976
Original token count: 0
Output:


Approved command prefix saved:
- ["rm", "-rf", "/tmp/regression-run-27553797634"]


Output:
Chunk ID: 8e61db
Wall time: 0.0000 seconds
Process exited with code 0
Original token count: 0
Output:


find /tmp/regression-run-27553797634 -type f | sort | head -200


nl -ba app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt | sed -n '1,250p'; nl -ba app/src/androidTest/java/com/yorvana/testsupport/Camera.kt | sed -n '1,160p'


rg -n "fun awaitTagVisible|awaitTagVisible|waitUntil.*Tag|recordItem\(" app/src/androidTest/java app/src/main/java -g '*.kt'


git log --oneline --all -- app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt && git blame -L 276,347 -- app/src/androidTest/java/com/yorvana/regression/attachments/AttachmentRealOsRegressionTest.kt


Output:
Chunk ID: e3740b
Wall time: 0.2253 seconds
Process exited with code 0
Original token count: 3719
Output:
     1	package com.yorvana.regression.attachments
     2
     3	import android.content.Intent
     4	import androidx.compose.ui.test.assertIsEnabled
     5	import androidx.compose.ui.test.junit4.createAndroidComposeRule
     6	import androidx.compose.ui.test.onAllNodesWithTag
     7	import androidx.compose.ui.test.onAllNodesWithText
     8	import androidx.compose.ui.test.onNodeWithContentDescription
     9	import androidx.compose.ui.test.onNodeWithTag
    10	import androidx.compose.ui.test.onNodeWithText
    11	import androidx.compose.ui.test.performClick
    12	import androidx.compose.ui.test.performScrollTo
    13	import androidx.compose.ui.test.performTextInput
    14	import androidx.test.espresso.intent.Intents
    15	import androidx.test.ext.junit.runners.AndroidJUnit4
    16	import androidx.test.platform.app.InstrumentationRegistry
    17	import androidx.test.uiautomator.UiDevice
    18	import com.google.common.truth.Truth.assertThat
    19	import com.yorvana.MainActivity
    20	import com.yorvana.R
    21	import com.yorvana.testsupport.Camera
    22	import com.yorvana.testsupport.ExternalViewer
    23	import com.yorvana.testsupport.FixtureAssetsRule
    24	import com.yorvana.testsupport.RetryRule
    25	import com.yorvana.testsupport.SystemPicker
    26	import com.yorvana.testsupport.VaultFixture
    27	import com.yorvana.testsupport.app
    28	import com.yorvana.testsupport.awaitTagVisible
    29	import com.yorvana.testsupport.awaitText
    30	import com.yorvana.testsupport.installFreshVault
    31	import com.yorvana.testsupport.killAndRelaunch
    32	import com.yorvana.testsupport.resetYorvanaState
    33	import com.yorvana.testsupport.seedAttachmentFile
    34	import com.yorvana.testsupport.seedRecord
    35	import com.yorvana.testsupport.seedVehicle
    36	import com.yorvana.testsupport.sha256
    37	import com.yorvana.testsupport.tiers.RegressionFull
    38	import com.yorvana.ui.TestTags
    39	import kotlinx.coroutines.runBlocking
    40	import org.junit.After
    41	import org.junit.Assume.assumeTrue
    42	import org.junit.Before
    43	import org.junit.Rule
    44	import org.junit.Test
    45	import org.junit.rules.RuleChain
    46	import org.junit.runner.RunWith
    47
    48	@RunWith(AndroidJUnit4::class)
    49	@RegressionFull
    50	class AttachmentRealOsRegressionTest {
    51	    val retryRule = RetryRule()
    52	    val composeRule = createAndroidComposeRule<MainActivity>()
    53	    val fixtureRule = FixtureAssetsRule()
    54
    55	    @get:Rule
    56	    val ruleChain: RuleChain = RuleChain.outerRule(retryRule).around(fixtureRule).around(composeRule)
    57
    58	    private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
    59
    60	    @Before
    61	    fun setup() {
    62	        composeRule.resetYorvanaState()
    63	        composeRule.installFreshVault()
    64	        Intents.init()
    65	    }
    66
    67	    @After
    68	    fun tearDown() {
    69	        runCatching { Intents.release() }
    70	        // Restore network if changed by any test
    71	        com.yorvana.testsupport
    72	            .networkState()
    73	            .restore()
    74	    }
    75
    76	    @Test
    77	    fun rA01_realSystemFilePicker_pdfPersistsAndViewIntentLaunches() {
    78	        val vehicle = composeRule.seedVehicle(nickname = "Civic")
    79	        val record = composeRule.seedRecord(vehicleId = vehicle.id)
    80
    81	        // Stage the fixture file to Downloads
    82	        val fixture = fixtureRule.copyUniquePdfToDownloads("R-A01")
    83
    84	        openVehicle("Civic")
    85	        openRecord(record)
    86
    87	        composeRule.onNodeWithTag(TestTags.EDIT_BUTTON).performClick()
    88	        composeRule.awaitTagVisible(TestTags.ATTACHMENT_PICK_FILE_BUTTON, timeoutMillis = 60_000)
    89
    90	        composeRule.onNodeWithTag(TestTags.ATTACHMENT_PICK_FILE_BUTTON).performScrollTo().performClick()
    91	        SystemPicker.pickFile(device, fixture.displayName)
    92
    93	        // Wait for the picker to fully close and app to resume
    94	        device.waitForIdle()
    95	        Thread.sleep(5000)
    96
    97	        // Wait for the attachment to appear in the UI list
    98	        val endTime = System.currentTimeMillis() + 30_000
    99	        var found = false
   100	        while (System.currentTimeMillis() < endTime) {
   101	            try {
   102	                if (composeRule.onAllNodesWithTag(TestTags.ATTACHMENT_ITEM).fetchSemanticsNodes().isNotEmpty()) {
   103	                    found = true
   104	                    break
   105	                }
   106	            } catch (_: Exception) {
   107	            }
   108	            Thread.sleep(500)
   109	        }
   110	        if (!found) error("Timeout waiting for attachment item to appear")
   111	        Thread.sleep(1000)
   112
   113	        // Ensure Save button is visible and enabled
   114	        composeRule.awaitTagVisible(TestTags.SAVE_BUTTON, timeoutMillis = 60_000)
   115
   116	        // Persistent save click to handle potential event drops on slow GMD
   117	        var saved = false
   118	        for (i in 1..3) {
   119	            val nodes = composeRule.onAllNodesWithTag(TestTags.SAVE_BUTTON).fetchSemanticsNodes()
   120	            if (nodes.isEmpty()) {
   121	                saved = true
   122	                break
   123	            }
   124	            try {
   125	                composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
   126	                composeRule.waitForIdle()
   127	                Thread.sleep(2000)
   128	                // Check if we navigated back (vehicle title is visible)
   129	                if (composeRule.onAllNodesWithText("Civic").fetchSemanticsNodes().isNotEmpty()) {
   130	                    saved = true
   131	                    break
   132	                }
   133	            } catch (_: Exception) {
   134	            } catch (_: AssertionError) {
   135	            }
   136	        }
   137
   138	        if (!saved) {
   139	            // Final wait attempt
   140	            composeRule.awaitText("Civic", timeoutMillis = 120_000)
   141	        }
   142
   143	        composeRule.killAndRelaunch()
   144	        openVehicle("Civic")
   145	        openRecord(record)
   146
   147	        val attachmentFile = composeRule.app().vaultStorage.resolveAttachmentDocumentFile(vehicle.id, record.id, fixture.displayName)
   148	        assertThat(attachmentFile).isNotNull()
   149	        val bytes =
   150	            composeRule.activity.contentResolver
   151	                .openInputStream(attachmentFile!!.uri)
   152	                ?.use { it.readBytes() }
   153	        assertThat(sha256(bytes ?: ByteArray(0))).isEqualTo(fixture.sha256)
   154
   155	        assertExternalViewIntentLaunches("application/pdf") {
   156	            composeRule.onNodeWithTag(TestTags.recordDetailAttachmentThumbnail(fixture.displayName)).performClick()
   157	        }
   158	    }
   159
   160	    @Test
   161	    fun rA02_realCameraCapture_photoPersistsAsJpgInVault() {
   162	        val vehicle = composeRule.seedVehicle(nickname = "Camera Car")
   163
   164	        openAddRecord("Camera Car")
   165	        fillMinimalRecord("22345", "Camera Shop")
   166	        composeRule.onNodeWithTag(TestTags.ATTACHMENT_TAKE_PHOTO_BUTTON).performScrollTo().performClick()
   167	        Camera.captureAndAccept(device)
   168
   169	        // Wait for the attachment to appear in the UI list
   170	        val endTime = System.currentTimeMillis() + 30_000
   171	        var found = false
   172	        while (System.currentTimeMillis() < endTime) {
   173	            try {
   174	                if (composeRule.onAllNodesWithTag(TestTags.ATTACHMENT_ITEM).fetchSemanticsNodes().isNotEmpty()) {
   175	                    found = true
   176	                    break
   177	                }
   178	            } catch (_: Exception) {
   179	            }
   180	            Thread.sleep(500)
   181	        }
   182	        if (!found) error("Timeout waiting for attachment item to appear")
   183
   184	        // Ensure Save button is enabled and click it
   185	        composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).assertIsEnabled().performClick()
   186
   187	        // Wait for it to navigate back to Vehicle Detail to ensure save is complete.
   188	        // Use 120s because GMD can be extremely slow during IO/navigation transitions.
   189	        composeRule.awaitTagVisible(TestTags.ADD_RECORD_FAB, timeoutMillis = 120_000)
   190
   191	        composeRule.killAndRelaunch()
   192	        openVehicle("Camera Car")
   193	        val recordId =
   194	            runBlocking {
   195	                composeRule
   196	                    .app()
   197	                    .vaultStorage
   198	                    .listRecordIds(vehicle.id)
   199	                    .getOrThrow()
   200	                    .single()
   201	            }
   202
   203	        val record =
   204	            runBlocking {
   205	                composeRule.app().recordRepository.getRecord(vehicle.id, recordId)
   206	            }
   207	        assertThat(record).isNotNull()
   208	        openRecord(record!!)
   209	        assertThat(record.attachments.single()).endsWith(".jpg")
   210	    }
   211
   212	    @Test
   213	    fun rA04_nonImageAttachment_launchesSystemViewerIntent() {
   214	        val vehicle = composeRule.seedVehicle(nickname = "Civic")
   215	        val record = composeRule.seedRecord(vehicleId = vehicle.id)
   216	        composeRule.seedAttachmentFile(vehicle.id, record.id, "test.pdf")
   217
   218	        openVehicle("Civic")
   219	        openRecord(record)
   220
   221	        assertExternalViewIntentLaunches("application/pdf") {
   222	            composeRule.onNodeWithTag(TestTags.recordDetailAttachmentThumbnail("test.pdf")).performClick()
   223	        }
   224	    }
   225
   226	    @Test
   227	    fun rA05_removeAttachmentWithConfirmation_deletesFileFromVaultFixture() {
   228	        val vehicle = composeRule.seedVehicle(nickname = "Remove Attachment Car")
   229	        val record = composeRule.seedRecord(vehicleId = vehicle.id, attachments = listOf("delete-me.txt"))
   230	        composeRule.seedAttachmentFile(vehicle.id, record.id, "delete-me.txt")
   231	        assertThat(VaultFixture.active().exists("vehicles/${vehicle.id}/records/${record.id}/delete-me.txt")).isTrue()
   232
   233	        openVehicle("Remove Attachment Car")
   234	        openRecord(record)
   235	        composeRule.onNodeWithContentDescription(composeRule.activity.getString(R.string.record_edit)).performClick()
   236	        composeRule.awaitText(composeRule.activity.getString(R.string.record_edit), timeoutMillis = 60_000)
   237	        composeRule.onNodeWithTag(TestTags.removeAttachment("delete-me.txt")).performScrollTo().performClick()
   238	        composeRule.awaitTagVisible(TestTags.CONFIRM_DELETE_BUTTON, timeoutMillis = 60_000)
   239	        composeRule.onNodeWithTag(TestTags.CONFIRM_DELETE_BUTTON).performClick()
   240	        composeRule.onNodeWithTag(TestTags.SAVE_BUTTON).performClick()
   241
   242	        assertThat(VaultFixture.active().exists("vehicles/${vehicle.id}/records/${record.id}/delete-me.txt")).isFalse()
   243	    }
   244
   245	    @Test
   246	    fun rA06_attachmentStillResolvableAfterVaultMoveMidSession() {
   247	        val vehicle = composeRule.seedVehicle(nickname = "Move Attachment Car")
   248	        val record = composeRule.seedRecord(vehicleId = vehicle.id, attachments = listOf("after-move.pdf"))
   249	        composeRule.seedAttachmentFile(vehicle.id, record.id, "after-move.pdf", "%PDF-1.4\nfixture")
   250
     1	package com.yorvana.testsupport
     2
     3	import androidx.test.platform.app.InstrumentationRegistry
     4	import androidx.test.uiautomator.By
     5	import androidx.test.uiautomator.UiDevice
     6	import androidx.test.uiautomator.Until
     7
     8	object Camera {
     9	    private const val TIMEOUT_MILLIS = 10_000L
    10
    11	    fun captureAndAccept(device: UiDevice) {
    12	        dismissDialogsAndPermissions(device)
    13
    14	        val shutter =
    15	            device.wait(Until.findObject(By.res("com.android.camera2:id/shutter_button")), TIMEOUT_MILLIS)
    16	                ?: device.wait(Until.findObject(By.descContains("Shutter")), TIMEOUT_MILLIS)
    17	                ?: device.wait(Until.findObject(By.descContains("Take photo")), TIMEOUT_MILLIS)
    18	                ?: error("Camera shutter control not found")
    19	        shutter.click()
    20
    21	        val done =
    22	            device.wait(Until.findObject(By.res("com.android.camera2:id/done_button")), TIMEOUT_MILLIS)
    23	                ?: device.wait(Until.findObject(By.descContains("Done")), TIMEOUT_MILLIS)
    24	                ?: device.wait(Until.findObject(By.text("OK")), TIMEOUT_MILLIS)
    25	                ?: device.wait(Until.findObject(By.textContains("Use photo")), TIMEOUT_MILLIS)
    26	                ?: device.wait(Until.findObject(By.text("Done")), TIMEOUT_MILLIS)
    27	        done?.click()
    28	    }
    29
    30	    fun captureAndCancel(device: UiDevice) {
    31	        dismissDialogsAndPermissions(device)
    32	        device.pressBack()
    33	        val discard =
    34	            device.wait(Until.findObject(By.textContains("Discard")), TIMEOUT_MILLIS)
    35	                ?: device.wait(Until.findObject(By.textContains("Delete")), TIMEOUT_MILLIS)
    36	                ?: device.wait(Until.findObject(By.textContains("Cancel")), TIMEOUT_MILLIS)
    37	                ?: run {
    38	                    val packageName = InstrumentationRegistry.getInstrumentation().targetContext.packageName
    39	                    if (device.wait(Until.hasObject(By.pkg(packageName)), TIMEOUT_MILLIS)) {
    40	                        return
    41	                    }
    42	                    error("Camera discard control not found")
    43	                }
    44	        discard.click()
    45	    }
    46
    47	    private fun dismissDialogsAndPermissions(device: UiDevice) {
    48	        device.waitForIdle()
    49	        repeat(2) {
    50	            if (Permissions.acceptIfShown(device)) {
    51	                device.waitForIdle()
    52	            }
    53	        }
    54	        val locationOk =
    55	            device.wait(Until.findObject(By.text("OK")), 1000)
    56	                ?: device.wait(Until.findObject(By.text("Next")), 1000)
    57	                ?: device.wait(Until.findObject(By.textContains("Remember")), 1000)
    58	        locationOk?.click()
    59	        device.waitForIdle()
    60	    }
    61	}