Privacy-Period-Tracker/docs/qa/ClaudeQACoverage.md

88 lines
4.6 KiB
Markdown

# Claude QA Coverage — Period
```
Status: Current
Owner: _null
Last reviewed: 2026-08-18
Governs: what each QA pass actually reached
Review trigger: Any QA round run
```
> Pass by pass, what was reached and what was not. The point of this file is the
> **Blocked** and **Not run** rows: a pass left out of a report reads exactly
> like a pass that succeeded, and that is how untested code ships believing it
> was tested.
## Round 1 — 2026-08-18 at `adc5075`, partial
Not a full round. Batch 01 produced the first build, and this covered the two
passes that build could support. Recorded as partial rather than left out,
because a pass omitted from a report reads exactly like a pass that succeeded.
**Environment:** emulator `PeriodQA`, API 36 (`sdk_gphone64_x86_64`), Pixel 6
profile, debug build.
| Pass | Result | Notes |
| --- | --- | --- |
| A — First run | **Pass** | Clean install, cold start, empty state reads correctly ("No forecast yet", "No periods logged yet"), all four tabs reachable. No notification permission prompt yet — none is requested until Batch 05. |
| B — Core loop | **Pass** | Logged a period; cycle day, countdown, window and confidence appeared. Edited the start date twice; forecast moved from 15 Sep to 13 Sep and the record was marked `edited`. Deletion covered by instrumented test rather than by hand. |
| C — Failure paths | **Partial** | Only one path exercised, and it found a crash — see below. Airplane mode, denied permissions and a killed process mid-entry were not tried. |
| D — Persistence and migration | **Partial** | Covered by an instrumented test that closes and reopens a file-backed database, which is what a force-stop does. A real force-stop, a reboot and an update-over-install were not tried. |
| E — Forecast under hard histories | **Not run** | The §51 cases pass as unit tests; none has been driven through the UI, which is what this pass is for. |
| F — Notification privacy on a lock screen | **Not run** | Nothing sends a notification yet — Batch 05. |
| G — Accessibility | **Not run** | TalkBack, font scaling and greyscale legibility untried. The working surface is not the designed screen, so this is worth deferring to Batch 03 rather than testing a screen that is about to be replaced. |
| H — Data ownership and leakage | **Partial** | Delete-all covered by an instrumented test. Export, app lock and the built-artifact inspection do not exist yet. |
### What pass C found
Tapping **Started today** twice on the same day killed the app:
```text
FATAL EXCEPTION: main
android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed:
period_records.startDate
```
Not filed as an issue: it was found and fixed inside the same batch, before any
build left this repository, and a tracker issue closed by the commit that
introduced the code would be bookkeeping rather than a record. It is written
down here, in the commit that fixed it, and in
[`../architecture/README.md`](../architecture/README.md), with four regression
tests pinning the behaviour.
The interesting part is not the crash. It is that **the first thing tried by
hand broke immediately**, on the app's primary button, with 70 unit tests
green — which is the argument for pass C existing at all.
### What lint found, that no pass would have
Wiring the boundary guard into `./gradlew check` ran Android lint for the first
time, on `f5e9fbe`:
```text
NewApi: java.time.LocalDate#ofInstant requires API 34 (minSdk is 26)
NewApi: java.time.LocalDate#EPOCH requires API 34 (minSdk is 26)
```
Both on the recalculation path — a crash on every device below Android 14.
Invisible to the unit tests and invisible to an API 36 emulator. Fixed, and
worth recording as a standing gap below: **this project has no test on a device
at its own `minSdk`.**
## Standing gaps
Things no round has ever covered, carried forward until they are. This list
existing is not a failure; it not existing while the gaps do is.
- **No device at `minSdk`.** Everything so far ran on API 36. The two NewApi
bugs above would have crashed on Android 8 through 13 and nothing except lint
could see them. Lint is a good guard and it is not a substitute for one run on
an old API level.
- **Physical-device coverage is undecided.** Passes F and G need a real device
with a lock screen and TalkBack; which device that is has not been chosen, and
an emulator is not a substitute for either.
- **Long-horizon accuracy** — whether predictions measurably improve at 3, 6 and
12 confirmed cycles — cannot be reached by a QA round at all. It needs either
a simulated history harness or real elapsed time, and until one exists the
product's headline claim is tested only at the unit level.