# Fix: @RegressionFull Results Missing from Dashboard

## Root Cause

`regressionFullCoreCheck` and `regressionFullExtendedCheck` are both `GradleBuild`
wrapper tasks that invoke `:app:pixel2api33DebugAndroidTest` in separate child
Gradle processes. Both write JUnit XML results to the **same** output directory:

```
app/build/outputs/androidTest-results/managedDevice/
```

Gradle runs them sequentially (no explicit ordering), and **whichever runs last
overwrites the other's XMLs**. Evidence from the dashboard (all `R-D*`, `R-A*`,
`R-S02`, `R-L06` entries show `N/A`) confirms `regressionFullCoreCheck` runs
last, leaving only `@Regression`-tier XMLs in the upload artifact. The
`@RegressionFull` results from `regressionFullExtendedCheck` are silently lost
before the CI artifact upload.

---

## Proposed Changes

### Fix 1 — Preserve @Regression results before @RegressionFull overwrites them

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

After the `regressionFullCoreCheck` and `regressionFullExtendedCheck` task
registrations (line 366), add:

1. A `Copy` task `preserveRegressionCoreResults` that:
   - `dependsOn(regressionFullCoreCheck)`
   - Copies `app/build/outputs/androidTest-results/managedDevice/` →  
     `app/build/outputs/androidTest-results/regression-core-preserved/`
2. `regressionFullExtendedCheck.configure { mustRunAfter(preserveRegressionCoreResults) }`  
   — guarantees deterministic ordering: core → preserve → extended.
3. `regressionFullCheck` gains an additional `dependsOn(preserveRegressionCoreResults)`.

Execution order after the change:
```
regressionFullCoreCheck  →  preserveRegressionCoreResults  →  regressionFullExtendedCheck
        (@Regression XMLs)        (safe copy)                   (@RegressionFull XMLs)
               ↓                      ↓                                ↓
        managedDevice/    regression-core-preserved/          managedDevice/  (overwrites, but that's ok now)
```

---

### Fix 2 — Manual `workflow_dispatch` flag to run the full suite

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

Add a boolean input to the `workflow_dispatch` trigger:

```yaml
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 * * *'
```

Update the two run-step conditions:

```yaml
# Core-only step (non-schedule, non-full-manual)
- name: Run Regression Check
  if: github.event_name != 'schedule' && !(github.event_name == 'workflow_dispatch' && inputs.run_full)
  run: ./gradlew regressionCheck ...

# Full step (schedule OR manual with run_full=true)
- name: Run Nightly Regression Checks
  if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && inputs.run_full)
  run: ./gradlew regressionFullCheck ...
```

Update the `aggregate-results` job `if:` condition to also fire on a full manual run:

```yaml
if: >
  always() &&
  (
    github.event_name == 'schedule' ||
    (github.event_name == 'workflow_dispatch' && inputs.run_full)
  )
```

---

### Fix 3 — Upload the preserved directory in CI

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

Add `app/build/outputs/androidTest-results/regression-core-preserved/` to the
"Upload Managed Device Reports" artifact step, alongside the
existing `managedDevice/` path.

---

### Fix 4 — Broaden the aggregator's XML discovery

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

Change the primary glob in `parse_xml_reports()` (line 187) to also scan the
preserved directory:

```python
# Before
xml_files = glob.glob("**/managedDevice/**/*.xml", recursive=True)

# After
xml_files = (
    glob.glob("**/managedDevice/**/*.xml", recursive=True)
    + glob.glob("**/regression-core-preserved/**/*.xml", recursive=True)
)
```

The `if not xml_files` fallback is preserved as-is.

---

## Bonus Fix: Deduplicate R-S02 scenario IDs

The research also surfaced that `VaultRealOsRegressionTest` has three methods
that all start with `rS02_`, which all resolve to scenario ID `R-S02` under the
naming convention. Only the last XML entry wins per run, silently dropping two
results. The correct fix is to add explicit `@ScenarioId` annotations:

- `rS02_movePath_…` → `@ScenarioId("R-S02a")`
- `rS02_startFreshPath_…` → `@ScenarioId("R-S02b")`
- `rS02_abortAfterCopyStarts_…` → `@ScenarioId("R-S02c")`

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

Add `@ScenarioId("R-S02a/b/c")` annotations before each `rS02_*` test method.

---

## Verification Plan

### Automated Tests
- `./gradlew testDebugUnitTest` — to confirm the aggregator's Python test suite
  (`test_aggregate_regression.py`) still passes (tests parse_xml_reports logic).
- `./gradlew ktlintCheck detekt lintDebug` — code quality.

### Manual Verification
- Locally dry-run `regressionFullCheck` (or simulate with two sequential runs) and
  confirm `regression-core-preserved/` is populated with `@Regression`-tier XMLs
  while `managedDevice/` contains `@RegressionFull`-tier XMLs.
- Run `python3 tools/aggregate_regression.py` from the project root (with both
  directories populated) and confirm both `R-V*` and `R-D*`/`R-A*` scenario IDs
  appear in the output JSON.

## Open Questions

> [!NOTE]
> The duplicate `R-S02` fix uses `R-S02a/b/c` suffixes. If you prefer `R-S02`,
> `R-S02-fresh`, `R-S02-abort` or another convention, let me know before I
> proceed.
