# Discrepancy Analysis: `plan-regression-tests.md` vs. Implemented Code

> [!NOTE]
> This document lists every discrepancy found between
> [plan-regression-tests.md](file:///home/Messier82/projects/service-book/docs/plan-regression-tests.md) and the actual
> implemented code. **Nothing has been fixed** — this is an audit only.

---

## 🔴 Critical Discrepancies

These are factually wrong statements in the doc that contradict reality.

### 1. Status banner says "No tests implemented yet"

**Doc** (L3):
> **Status:** Planning. No tests implemented yet — see §4 Phased implementation.

**Reality**: All phases are implemented — 42+ test methods across 11 test classes, the macrobenchmark module, CI
workflows, the dashboard, the promotion playbook, and the aggregation pipeline are all live and running. The dashboard
records 4 historical runs.

---

### 2. `@Quarantined` is described as not yet carrying metadata parameters

**Doc** (§4 Phase 7, L209):
> Promote `@Quarantined` to carry `since: String` (ISO-8601), `issue: Int`, `reason: String`

**Reality**: [Quarantined.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/testsupport/tiers/Quarantined.kt)
already has all three parameters (`since`, `issue`, `reason`). This is implemented, not future work.

---

### 3. `@ScenarioId` is described as a future Phase 7 item

**Doc** (§4 Phase 7, L211):
> Adopt spec IDs … via `@ScenarioId("R-A01")` on the test method

**Reality**: [ScenarioId.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/testsupport/tiers/ScenarioId.kt)
exists and is already in use on several tests:
- [LifecycleRobustnessRegressionTest.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/lifecycle/LifecycleRobustnessRegressionTest.kt#L139) — `@ScenarioId("R-L04")`
- [VaultRealOsRegressionTest.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt#L89) — `@ScenarioId("R-S02a")`, `@ScenarioId("R-S02b")`, `@ScenarioId("R-S02c")`
- [BillingSandboxRegressionTest.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/billing/BillingSandboxRegressionTest.kt#L88) — `@ScenarioId("R-P04")`

---

### 4. Macrobenchmark module described as Phase 6 future work

**Doc** (§4 Phase 6, L196–205):
> New `:macrobenchmark` module … M-01..04. Baseline thresholds calibrated over 5 runs

**Reality**: The [macrobenchmark module](file:///home/Messier82/projects/service-book/macrobenchmark) exists and is fully
wired:
- [YorvanaBenchmark.kt](file:///home/Messier82/projects/service-book/macrobenchmark/src/main/java/com/yorvana/macrobenchmark/YorvanaBenchmark.kt) implements M-01 through M-04
- `regressionFullCheck` already [depends on](file:///home/Messier82/projects/service-book/app/build.gradle.kts#L397) `:macrobenchmark:pixel2api33BenchmarkAndroidTest`
- The [regression-history.json](file:///home/Messier82/projects/service-book/docs/regression-history.json) records passing M-01..M-04 results
- A [README.md](file:///home/Messier82/projects/service-book/macrobenchmark/README.md) documents the threshold policy

---

### 5. Phase 1 §4 describes items as not yet existing

**Doc** (§4 Phase 1, L154–163): Lists all Phase 1 deliverables as future work:
- "Add custom marker annotations" → **All 6 exist** (`@Smoke`, `@Regression`, `@RegressionFull`, `@BillingSandbox`, `@Quarantined`, `@ScenarioId`)
- "Wire `regressionCheck` and `regressionFullCheck` Gradle tasks" → **Both exist** using the GradleBuild child invocation mechanism
- "Smoke isolation via positive filter" → **Implemented** at [app/build.gradle.kts L309-317](file:///home/Messier82/projects/service-book/app/build.gradle.kts#L309)
- "Add UiAutomator helpers in `testsupport/`" → **All exist** (`SystemPicker`, `Camera`, `Permissions`, `ExternalViewer`)
- "Add the annotation-misuse guardrail" → **Exists** as [RegressionTierScannerTest.kt](file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/testsupport/RegressionTierScannerTest.kt)
- "Add the nightly workflow pair" → **Both exist** (`regression.yml`, `regression-label.yml`)

---

### 6. All phases (2–7) described as future work

**Doc** (§4 Phase 2–7, L165–216): Every phase reads as future deliverables.

**Reality**: All scenarios from Phases 1–6 are implemented and running. Phase 7 items (quarantine metadata, `@ScenarioId`,
orphan-quarantine guard, durable history, promotion playbook) are also either fully or substantially implemented.

---

## 🟡 Moderate Discrepancies

These are areas where the doc's specifics differ from what was actually built.

### 7. R-D01 is in `setup/` not `vault/` — plan says "§3.4 Vault / SAF"

**Doc** (§3.4, L93):
> R-D01 — Fresh setup: drive real SAF folder picker via UiAutomator

**Reality**: R-D01 lives in [SetupRegressionTest.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/setup/SetupRegressionTest.kt) at `regression/setup/`, not `regression/vault/` or
anywhere in the vault directory. The plan's §2 table suggests SAF picker tests live in `androidTest/.../regression/system/`.

---

### 8. Plan says "Phase 1 will pick one" mechanism — but doesn't say which was chosen

**Doc** (§2 L48–50):
> Two viable shapes; Phase 1 will pick one:
> (a) `GradleBuild` child invocations …
> (b) Variant- or device-scoped tasks …

**Reality**: Option (a) — `GradleBuild` child invocations — was chosen, as seen in
[registerRegressionDeviceTask](file:///home/Messier82/projects/service-book/app/build.gradle.kts#L325) which uses
`tasks.register<GradleBuild>(taskName)`. The doc still presents both as open options.

---

### 9. R-D03 tests vault folder deletion, not permission revocation

**Doc** (§3.4, L95):
> R-D03 — Vault permission revoked externally (clear persistable URI perms) → app shows recoverable error state

**Reality**: [VaultRealOsRegressionTest.rD03](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt#L63-L76) deletes the entire vault directory (`root.deleteRecursively()`) rather than revoking URI permissions. The test asserts "no crash on relaunch" but doesn't verify "recoverable error state" — it just calls `killAndRelaunch()` with no assertion after.

---

### 10. R-D05 (Migration) is listed in §3.4 but doesn't exist

**Doc** (§3.4, L97):
> R-D05 — Migration: pre-seed an old-format vault, verify `MigrationHelper` upgrades it

**Reality**: No R-D05 test exists anywhere in the codebase. No `regression-fixtures/legacy-vault-v0/` directory exists.
The plan itself notes this is conditional on a concrete legacy schema fixture being landed, but it's still listed as a
scenario in §3.4 without being marked as unimplemented.

---

### 11. Dashboard lists `scaffoldIsDiscoverable` — not in the plan's coverage map

**Doc** (§3, L58–150): The coverage map lists R-V01..07, R-R01..04, R-A01..07, R-D01..05, R-C01..03, R-S01..04,
R-P01..05, R-L01..07, M-01..04.

**Reality**: [RegressionScaffoldTest.scaffoldIsDiscoverable](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/RegressionScaffoldTest.kt) is a no-op test (`= Unit`) that exists and runs
(present in the dashboard), but is never mentioned in the plan. It's clearly a Phase 1 infrastructure test but is not
documented.

---

### 12. R-A03 tier discrepancy

**Doc** (§3.3, L85):
> R-A03 — Image viewer gestures … **Owned by Phase 4 (#117)**

The plan doesn't explicitly state the tier for R-A03, but Phase 4 discussion (L182) says: "long parametrized runs carry
`@RegressionFull`, one canonical rotation + one canonical kill stay in `@Regression`".

**Reality**: [ImageViewerGestureRegressionTest](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/attachments/ImageViewerGestureRegressionTest.kt#L35) has `@Regression` at class level, and the
[dashboard](file:///home/Messier82/projects/service-book/docs/regression-dashboard.md#L31) lists R-A03 as `Regression`
tier. This is consistent but the doc doesn't explicitly confirm the tier assignment for R-A03.

---

### 13. R-S02 three paths are in `VaultRealOsRegressionTest` not a settings test

**Doc** (§3.6, L108):
> R-S02 — Change vault folder via Settings

**Reality**: The three R-S02 tests (a/b/c) live in
[VaultRealOsRegressionTest](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/vault/VaultRealOsRegressionTest.kt)
alongside vault tests (R-D02..D04), not in a settings test class. The class is annotated `@RegressionFull`, so all three
R-S02 variants run under RegressionFull tier. The plan specifies R-S02 as Phase 3 which is consistent with it living
alongside other real-SAF scenarios, but the doc's §3.6 groups it under "Settings".

---

### 14. `regressionFullCheck` now includes macrobenchmark — plan says Phase 6 will add it

**Doc** (§2 L47):
> the `:macrobenchmark` `dependsOn` is deferred to **Phase 6 (#119)**

**Reality**: [regressionFullCheck](file:///home/Messier82/projects/service-book/app/build.gradle.kts#L394-L398) already
`dependsOn(regressionFullCoreCheck, regressionFullExtendedCheck, macrobenchmarkTaskPath)`. The macrobenchmark dependency
is live, not deferred.

---

### 15. Plan describes annotation-misuse guardrail implementation options — only one was chosen

**Doc** (§4 Phase 1, L162):
> source-level scanner under `src/test` OR Gradle bytecode task scanning `compileDebugAndroidTestKotlin` output via ASM

**Reality**: The source-level scanner approach was chosen — implemented as a private object `RegressionTierScanner`
inside [RegressionTierScannerTest.kt](file:///home/Messier82/projects/service-book/app/src/test/java/com/yorvana/testsupport/RegressionTierScannerTest.kt#L267). The doc still presents both options.

---

### 16. M-04 approach: plan says choose between TraceSectionMetric and FrameTimingMetric

**Doc** (§3.9, L150):
> Either `TraceSectionMetric` against production `androidx.tracing` sections … **or** a `FrameTimingMetric`

**Reality**: [YorvanaBenchmark.M04](file:///home/Messier82/projects/service-book/macrobenchmark/src/main/java/com/yorvana/macrobenchmark/YorvanaBenchmark.kt#L83)
uses `FrameTimingMetric`. The [README](file:///home/Messier82/projects/service-book/macrobenchmark/README.md#L59-L62) explains
the choice was made (no reliable transition-work span). The plan still presents both options.

---

### 17. R-L06 plan specifies two backends (file-backed + SAF) — only file-backed is tested

**Doc** (§3.8, L138–142):
> Assertion split by vault backend … **File-backed** … **SAF** …

**Reality**: [StorageRobustnessRegressionTest](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/vault/StorageRobustnessRegressionTest.kt)
tests only the file-backed path via `VaultStorageImpl` with `Uri.fromFile(root)`. No SAF-backed R-L06 test exists.
The plan acknowledges this may narrow ("If either production change is descoped"), but the doc doesn't record which
was chosen.

---

### 18. Dashboard lists M-01..M-04 as `Regression` tier

**Dashboard** ([regression-dashboard.md L25-28](file:///home/Messier82/projects/service-book/docs/regression-dashboard.md#L25)):
> M-01 through M-04 all listed as tier `Regression`

**Plan** (§2, L42):
> | Macrobenchmark | separate module | Nightly | ~15 min |

The plan says macrobenchmarks are a "separate module" tier running nightly. The dashboard labels them `Regression`.
Since macrobenchmarks live in their own module and don't carry any app-tier annotation, labeling them `Regression` is
misleading — they're not filtered by `annotation=Regression` in CI.

---

### 19. Fixture ownership approach for macrobenchmark — plan listed three options

**Doc** (§4 Phase 6, L203):
> (a) extract seeding into a shared `:testfixtures` module,
> (b) expose a benchmark-variant-only test hook …
> (c) intentional duplication with sync requirement

**Reality**: Option (b) was chosen — `BenchmarkSeedActivity` is exposed only in the `benchmark` variant per the
[README](file:///home/Messier82/projects/service-book/macrobenchmark/README.md#L26-L29). The plan doesn't record the
decision.

---

### 20. `regressionFullSandboxCheck` described in §2 but doesn't exist in Gradle

**Doc** (§2, L54):
> `regressionFullSandboxCheck` — runs `@BillingSandbox` against the Play-enabled device path

**Reality**: No `regressionFullSandboxCheck` Gradle task exists. The plan itself later notes (L190) that
"Option 2 selected — external `billing-sandbox.yml` workflow … with the sandbox tier outside `regressionFullCheck`."
But §2 still describes the task as if it could exist. This should be cleaned up.

---

### 21. `preserveRegressionCoreResults` task not documented anywhere in the plan

**Reality**: [preserveRegressionCoreResults](file:///home/Messier82/projects/service-book/app/build.gradle.kts#L371-L389) is a `Copy` task that saves `@Regression` XML results before `regressionFullExtendedCheck` overwrites
the shared `managedDevice/` output directory. This is a necessary implementation detail but is nowhere in the plan.

---

## 🟢 Minor / Cosmetic Discrepancies

### 22. Plan references line numbers that may have drifted

**Doc** (L26): `app/build.gradle.kts:68` for `testBuildType`, (L43) for `testVariant`. These line references are brittle
and likely stale after code changes.

### 23. Plan uses `categories/` as a subpackage — code uses `settings/`

**Doc** (§3.5): Categories tests (R-C01..03) have their own section. In the code, they live inside
[SettingsCategoriesPaywallRegressionTest.kt](file:///home/Messier82/projects/service-book/app/src/androidTest/java/com/yorvana/regression/settings/SettingsCategoriesPaywallRegressionTest.kt) in the `settings/` package, alongside R-S01, R-S03, R-S04, and R-P05.

### 24. Plan says `testsupport/lifecycle/` for lifecycle helpers

**Doc** (§4 Phase 4, L184):
> Helpers under `testsupport/lifecycle/`: `ProcessDeath.recreate` … and `ProcessDeath.kill`

**Reality**: `ProcessDeath`, `LauncherReentry`, and `TrimMemory` do exist under `testsupport/lifecycle/` (confirmed by
imports in LifecycleRobustnessRegressionTest). ✅ This matches.

### 25. Plan says `testsupport/gestures/` for gesture helpers

**Doc** (§4 Phase 4, L184):
> Gesture helpers under `testsupport/gestures/`: `Gestures.pinch / .doubleTap / .swipe`

**Reality**: Confirmed by imports — `Gestures.pinch`, `Gestures.doubleTap`, `Gestures.swipe`, `Direction` all exist
under `testsupport/gestures/`. ✅ This matches.

### 26. Plan references issue numbers (#114–#120) as future contracts

Throughout the doc, phrases like "issue **#114**", "Phase 5 (#118)" are used to point to GitHub issues. These references
are correct as historical context but read as if the work is pending.

---

## ✅ What Matches Correctly

For completeness, these plan claims were verified as accurate:

| Claim | Status |
|---|---|
| 5 + 1 tier annotations with `@Inherited` and `RUNTIME` retention | ✅ All 6 match exactly |
| `GradleBuild` child invocation mechanism for regression tasks | ✅ |
| `regressionCheck` = `annotation=Regression, notAnnotation=Quarantined` | ✅ |
| `regressionFullCoreCheck` = `annotation=Regression` (no notAnnotation) | ✅ |
| `regressionFullExtendedCheck` = `annotation=RegressionFull` | ✅ |
| `SmokeTest.kt` carries `@Smoke` | ✅ |
| Smoke device uses positive `annotation=Smoke` filter | ✅ |
| `regression.yml` runs `regressionCheck` on non-nightly, `regressionFullCheck` on nightly | ✅ |
| `regression-label.yml` triggers on `regression` label | ✅ |
| `billing-sandbox.yml` runs on self-hosted runner with physical device | ✅ |
| Billing sandbox uses Option 2 (external workflow) | ✅ |
| R-V01..07 all implemented and `@Regression` tier | ✅ |
| R-R01..04 all implemented and `@Regression` tier | ✅ |
| R-C01..03 implemented | ✅ |
| R-S01, R-S03, R-S04 implemented | ✅ |
| R-P05 implemented as `@Regression` (not sandbox-dependent) | ✅ |
| R-L01..L05, R-L07 implemented | ✅ |
| R-L01 split: canonical `@Regression` + extended `@RegressionFull` | ✅ |
| R-L02 uses `ProcessDeath.kill` (not `force-stop`) | ✅ |
| R-A01..A02, R-A04..A07 implemented as `@RegressionFull` | ✅ |
| R-A03 implemented as `@Regression` | ✅ |
| R-D01..D04 implemented | ✅ |
| R-S02a/b/c implemented as `@RegressionFull` | ✅ |
| R-P03, R-P04 implemented as `@BillingSandbox` | ✅ |
| R-P01, R-P02 intentionally not reimplemented (covered by smoke) | ✅ |
| M-01..M-04 implemented in `:macrobenchmark` module | ✅ |
| Dashboard + history JSON + promotion playbook exist | ✅ |
| Aggregate pipeline (`tools/aggregate_regression.py`) exists | ✅ |
| No `@Quarantined` tests in production (consistent with dashboard) | ✅ |
| Annotation-misuse scanner with negative test fixtures | ✅ |

---

## Summary

| Category | Count |
|---|---|
| 🔴 Critical (doc says "not implemented" but it is) | 6 |
| 🟡 Moderate (specifics differ from implementation) | 15 |
| 🟢 Minor / Cosmetic | 5 |
| ✅ Verified correct | 35+ |

The primary theme: **the doc is frozen in a "planning" state and needs to be rewritten as "implemented documentation."**
All the "Phase X will…" language, the "Planning. No tests implemented yet" status banner, and the open decision points
need to be resolved to reflect the choices that were actually made.
