# Claude QA Report — Privacy: Period Tracker ``` Status: Current Owner: _null Last reviewed: 2026-08-21 Governs: the QA verdict — build SHAs, round summaries, the overall judgment Review trigger: Any QA round run ``` > The QA verdict. Companion to [`ClaudeQACoverage.md`](ClaudeQACoverage.md) > (what each pass reached) and [`ClaudeQAPlan.md`](ClaudeQAPlan.md) (the > playbook). > > **Defects are issues, not entries here.** A defect found in a round is filed > in the tracker with a severity label, where it can be assigned, closed by a > commit, and counted. This file keeps the part a tracker is bad at: a judgment > about whether the thing is fit to ship. ## Current run-state - **Last QA round:** Round 4 — 2026-08-18, partial (A and B pass; C, D, E, F, G and H partial). **No full round has run since**; the checks on the 20th and 21st were targeted at what those batches changed and are recorded below as such. - **Last verified build SHA:** `0ae92bd` — targeted device checks. The last SHA a full round covered is `f43e1c2`. - **Last tested device / environment:** emulator `PeriodQA`, API 36, debug build, for the targeted checks. The API 26 run (`PeriodMinSdk26`, Pixel 6 profile, font scales 1.0 / 1.3 / 2.0) was Round 4's and **has not been repeated since** — four batches have landed on top of it. - **Overall status:** Every batch through 12 has landed and the tracker has no open issue. What was found in the last two days is the reason not to read that as *finished*: two release-blockers were sitting in shipped code that no test and no round had caught — a reminder button that recorded a new period instead of ending one, corrupting the history and every forecast built on it, and a Change PIN flow that never checked the current PIN. Both are fixed and both were driven on a device. The export can now be read back, which was the last thing standing between a user and her own history on a new phone; that flow was driven end to end on `PeriodQA` today. The pattern is now five rounds old and unchanged: **every defect of consequence in this project has been found by driving the app, never by a test.** The tests are what stop them coming back. So the standing gaps are the verdict: nobody has yet looked at an actual locked screen, TalkBack has still never been run, and the app has not been launched at its own `minSdk` since four batches were written. Those three hours are what stands between "no known defect" and "fit to ship", and they are unspent. ## Open defects **Do not list them here, and do not read a defect count out of this file.** The Command Center's docs report parses this document for open `P0` / `P1` / `P2` counts, and under this convention they are always zero — the defects are in the tracker, which is the whole point. The zeros here mean *this file does not hold them*, never *there are none*. Filed as issues in this repository's tracker, labelled by what they cost: - **P0** — ships broken, or loses data - **P1** — materially wrong, but shippable - **P2** — cosmetic or low impact - **release-blocker** — a release built today would be *wrong*, not merely incomplete Severity is what it costs, not how annoying it is to fix. Every defect needs the build SHA it was found at — a finding that cannot be re-tested cannot be closed — so put it in the issue body. ## Round notes One entry per round, the verdict only. What each pass reached, and what it could not, lives in [`ClaudeQACoverage.md`](ClaudeQACoverage.md) and is deliberately not repeated here. - **Targeted checks — 2026-08-21 at `0ae92bd`.** Not a round: only the restore flow was driven, on `PeriodQA` (API 36). An archive pushed to Downloads was restored through Settings → Restore from a file → Add to what is here, giving *"Restored. 4 periods and 2 spotting days added."*; Insights then showed three cycles with accuracy still unscored, which is the property that matters — an import must not manufacture accuracy figures. The same file a second time gave *"Everything in that file was already recorded here, so nothing changed."* The replace confirmation was opened and cancelled. Verdict on this flow only: it does what its screen says. Passes A–H are untouched by this and remain at Round 4's state. - **Targeted checks — 2026-08-20 at `f91ba6e`.** Not a round: Batches 11 and 12's own surfaces were driven on `PeriodQA` — the four App-lock options rendering with the biometric rows correctly disabled and explained on a device with no enrolment, and the settings back arrow returning from every sub-page. Screenshots are black once a lock is set (FLAG_SECURE), so the evidence is `uiautomator dump` text. Verdict: the reported navigation trap is gone and the lock offers what it claims to offer. - **Round 4 — 2026-08-18 at `f43e1c2`, partial.** Batch 06 began, and driving it found two defects in controls that already looked finished: navigation labels wrapping mid-word at font scale 2.0, and a privacy option whose row was inert because only its radio button was clickable. Verdict: the privacy controls now behave as their own dialog promises — checked by setting a preference, deleting all data, and confirming the preference survived. - **Round 3 — 2026-08-18 at `0451fbe`, partial.** Fertility and reminders landed and pass F became runnable for the first time. Verdict: fit to keep building, not fit to ship — the privacy promise is proved against the notification object and never against a locked screen. Two defects, both in how Android behaves rather than in the app's logic, and both found by running on a device. - **Round 2 — 2026-08-18 at `19edf4c`, partial.** Every screen Batch 03 built was driven by hand. Verdict: the product became coherent, and three defects surfaced that no unit test would have caught — one of them dark mode, broken since Batch 01. - **Round 1 — 2026-08-18 at `adc5075`, partial.** Two passes were all the first build could support. Verdict: sound enough to keep going, and the first thing tried by hand crashed the app with 70 unit tests green — which is the argument for driving it at all.