# Walkthrough - Phase A Regression Setup (#132)

We successfully moved the regression test `rD01_freshSetup_realSafFolderPicker_completesSetup` into a new test suite designated for gating runs and verified its failure.

## Changes Made

### Instrumented Tests

- Created a new test file [SetupRegressionTest.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/setup/SetupRegressionTest.kt) under the package `com.yorvana.regression.setup`. This file contains the moved `rD01_freshSetup_realSafFolderPicker_completesSetup` test and is annotated with `@Regression` and `@RunWith(AndroidJUnit4::class)`.
- Modified [VaultRealOsRegressionTest.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt) to remove the duplicate `rD01_freshSetup_realSafFolderPicker_completesSetup` test.

---

## Verification & Validation Results

We executed the newly registered regression test using the following Gradle command on the Managed Device:

```bash
./gradlew app:pixel2api33DebugAndroidTest \
  -Pandroid.testInstrumentationRunnerArguments.class=com.yorvana.regression.setup.SetupRegressionTest \
  -Pandroid.testInstrumentationRunnerArguments.annotation=com.yorvana.testsupport.tiers.Regression
```

### Test Output

The execution of the test failed as expected, successfully catching the regression bug where the setup wizard goes back/loops on step 1:

```
com.yorvana.regression.setup.SetupRegressionTest > rD01_freshSetup_realSafFolderPicker_completesSetup[pixel2api33] FAILED
        androidx.compose.ui.test.ComposeTimeoutException: Condition still not satisfied after 30000 ms
        at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$AndroidComposeUiTestImpl.waitUntil(ComposeUiTest.android.kt:887)
...
Tests on pixel2api33 failed: There was 1 failure(s).
Finished 1 tests on pixel2api33
Execute com.yorvana.regression.setup.SetupRegressionTest.rD01_freshSetup_realSafFolderPicker_completesSetup: FAILED
```

This verifies that Phase A is complete and the gating regression suite now catches the empty-vault setup issue.

---

## Phase B — Bug Fix (#131)

### Change

Modified [MainActivity.kt](file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/MainActivity.kt) line 108:

```diff
-                context.restoreSavedNavRoute() ?: VehicleList
+                context.restoreSavedNavRoute()?.takeUnless { it == Setup } ?: VehicleList
```

This ensures that `Setup` is treated as an unrestorable boundary route. Once a vault URI is configured, any persisted `setup` route is discarded and navigation falls back directly to `VehicleList`, breaking the loop.

### Verification Results

**Instrumented test (`rD01`) — now passing:**
```
Starting 1 tests on pixel2api33
Finished 1 tests on pixel2api33
BUILD SUCCESSFUL
```

**JVM unit tests:** `BUILD SUCCESSFUL`

**Code quality (ktlint, detekt, lintDebug):** `BUILD SUCCESSFUL`

Also fixed a needless blank line in [VaultRealOsRegressionTest.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt) flagged by ktlint after the rD01 removal.

---

## Follow-up — @RegressionFull Dashboard Blind Spot

### Root cause

`regressionFullCoreCheck` and `regressionFullExtendedCheck` are both `GradleBuild` wrapper tasks that each invoke `:app:pixel2api33DebugAndroidTest` in a child Gradle process. Both write JUnit XML results to the same directory (`app/build/outputs/androidTest-results/managedDevice/`). The second task to run always overwrites the first's results. Because `regressionFullCoreCheck` ran last, only `@Regression`-tier XMLs survived in the CI artifact — `@RegressionFull` results (`R-D*`, `R-A*`, `R-S02*`) were silently dropped, leaving them as `N/A` in the dashboard forever.

### Changes

#### [app/build.gradle.kts](file:///home/Messier82/projects/service-book/app/build.gradle.kts)

Added a new `Copy` task `preserveRegressionCoreResults` that:
- `dependsOn(regressionFullCoreCheck)` — runs immediately after the @Regression run completes
- Copies `managedDevice/` → `regression-core-preserved/` before the @RegressionFull run can overwrite it
- `regressionFullExtendedCheck.mustRunAfter(preserveRegressionCoreResults)` — enforces deterministic ordering
- `regressionFullCheck` depends on `preserveRegressionCoreResults` to wire it into the graph

#### [regression.yml](file:///home/Messier82/projects/service-book/.github/workflows/regression.yml)

- Added `regression-core-preserved/` to the CI artifact upload so both tiers' XMLs reach the aggregator
- Added `run_full: boolean` input to `workflow_dispatch` — manual triggers can now opt into `regressionFullCheck` without waiting for the nightly cron
- Updated both run-step conditions and the `aggregate-results` job `if:` to fire on `run_full` manual triggers

#### [aggregate_regression.py](file:///home/Messier82/projects/service-book/tools/aggregate_regression.py)

Broadened `parse_xml_reports()` to scan both:
```python
glob("**/managedDevice/**/*.xml")  # @RegressionFull results (extended run)
glob("**/regression-core-preserved/**/*.xml")  # @Regression results (preserved)
```

#### [VaultRealOsRegressionTest.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt)

Added `@ScenarioId` annotations to the three `rS02_*` methods that previously all collapsed to the same dashboard key `R-S02`:
- `rS02_movePath_…` → `@ScenarioId("R-S02a")`
- `rS02_startFreshPath_…` → `@ScenarioId("R-S02b")`
- `rS02_abortAfterCopyStarts_…` → `@ScenarioId("R-S02c")`

### Verification

**Unit tests + ktlint + detekt + lintDebug:** `BUILD SUCCESSFUL`

---

## PR Review Updates

Based on PR feedback, the following refinements were made:

### Round 1
1. **Rule Visibility**: Marked `retryRule` and `composeRule` as `private` in `SetupRegressionTest.kt`.
2. **SystemPicker Import Clean-up**: Dropped the redundant FQN prefix for `SystemPicker.createAndPickFolder`.
3. **Gradle Task Redundancy**: Removed `regressionFullCoreCheck` from the `regressionFullCheck` task `dependsOn` (as it is transitively covered by `preserveRegressionCoreResults`).
4. **Preservation Robustness**: Configured `preserveRegressionCoreResults` with `doFirst { project.delete(destinationDir) }` to wipe target output before copying.
5. **CI Aggregator Explanation**: Added a comment explaining the custom aggregator condition in `.github/workflows/regression.yml`.

### Round 2
1. **Source-Side Setup Loop Fix**: Modified `toSavedRoute()` in [AppNavGraph.kt](file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/navigation/AppNavGraph.kt) to return `null` for the `Setup` route and removed it from `simpleSavedRoutes` so setup state is never persisted. Preserved `.takeUnless { it == Setup }` in `restoreSavedNavRoute()` as a defensive filter for legacy preferences, and simplified `MainActivity.kt`.
2. **Snapshot on Test Failure**: Changed the copy task wiring in [build.gradle.kts](file:///home/Messier82/projects/service-book/app/build.gradle.kts) to `regressionFullCoreCheck.finalizedBy(preserveRegressionCoreResults)` to guarantee core test reports are snapshotted even on failure nights.
3. **Aggregator Glob Ordering Defense**: Changed the glob search order in [aggregate_regression.py](file:///home/Messier82/projects/service-book/tools/aggregate_regression.py) to parse preserved core results first and added a `not in results` guard to guarantee core outcomes take precedence.
4. **Unit Tests**: Excluded `Setup` from the roundtrip codecs test in [NavRouteCodecTest.kt](file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/ui/navigation/NavRouteCodecTest.kt) and verified that it correctly returns `null` on save.

### Round 3 & BuildName Path Collision Fix
1. **CI Workflow Header**: Updated `.github/workflows/regression.yml` to reflect manual runs default to core but can run the full suite via `run_full`.
2. **Defensive Navigation Comment**: Added an explanatory comment above `?.takeUnless { it == Setup }` in [AppNavGraph.kt](file:///home/Messier82/projects/service-book/app/src/main/java/com/yorvana/ui/navigation/AppNavGraph.kt).
3. **GradleBuild Path Conflict Fix**: Configured a unique `buildName = taskName` on the `GradleBuild` tasks registered by `registerRegressionDeviceTask` in [build.gradle.kts](file:///home/Messier82/projects/service-book/app/build.gradle.kts). This prevents Gradle's configuration engine from colliding on identical build directories (such as `/home/runner/work/android/android` on CI) when multiple nested builds are executed sequentially in the same parent run.

