docs: record the minSdk emulator, and the first run at API 26

A second AVD exists — PeriodMinSdk26, API 26, Pixel 6 — and the app was built,
installed and driven on it end to end. Everything before today ran on API 36,
which the standing gaps list has said since Batch 01.

The QA plan now names both emulators, because rounds cite PeriodQA by name and
nothing defined it. It also carries the recreate command and three mechanical
traps that each cost time today:

- a windowed emulator dies with its session's X server (XIO: fatal IO error on
  X server ":0") and took one run down mid-flow; -no-window has no such
  dependency and screencap still works
- adb regularly has three devices attached on this machine, since other projects
  keep emulators running, so the serial must be resolved from the AVD name
  rather than assumed to be emulator-5554
- the debug build's application id carries a .debug suffix, so
  `monkey -p dev.privacyllc.period` aborts with "No activities found to run"
  after a successful install

The coverage doc's "No device at minSdk" gap is marked partly closed, with what
was reached (onboarding to first forecast, the date picker, all four tabs, a
relaunch after a cold boot) and what was not (notifications, the instrumented
suites, anything needing more than one cycle of history).

Also fixes a guard failure I introduced in ad085fb: that commit's log entry
named docs/architecture/scripts/forgejo-issue.py in backticks, and doc-claims.sh
reads a backticked path as a claim the file exists. I had run the guard before
writing the entry and not after. Named bare now, per the convention
DOC_TRUST_MAP.md already documents; doc-claims reports 255 paths, all present.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
null 2026-08-18 18:07:07 -05:00
parent 0d280d5415
commit 7f7513e7be
3 changed files with 115 additions and 9 deletions

View File

@ -32,6 +32,65 @@ written and stay true. It is exempt from review for the same reason a receipt is
## Entries ## Entries
### 2026-08-18 — The app ran at `minSdk` for the first time
A second emulator exists — `PeriodMinSdk26`, API 26, Pixel 6 — and the app was
built, installed and driven on it start to finish. Everything before today ran on
API 36, which the standing gaps list has said since Batch 01.
**It works.** Onboarding from *Get Started* to a first forecast, the Material 3
date picker, Today, Calendar, Insights and Settings, all rendering correctly on
Android 8.0. The forecast was right — 4 August plus 28 days is 1 September, an
11-day window, confidence honestly **Low** on one cycle of history — and
fertility declined to estimate rather than inventing one. No crash, and no
`NoSuchMethodError`, `VerifyError` or `NoClassDefFoundError` attributable to the
app. The one `NoClassDefFoundError` in logcat is
`com.google.android.googlequicksearchbox` failing to resolve `TracingController`,
an API 28 class, in its own process.
That is a genuinely useful negative result: the two `NewApi` bugs found in Batch
01 were exactly this shape and would have crashed here.
**Three mechanical traps, all now in the QA playbook** rather than in somebody's
memory:
1. **A windowed emulator dies with the session's X server.** The first one was
killed by `XIO: fatal IO error on X server ":0"` mid-flow. `-no-window` has no
such dependency and screenshots still work through `adb exec-out screencap`.
2. **`adb` regularly has three devices attached here**, because other projects
keep their own emulators running, and a bare `adb shell` then fails with *more
than one device/emulator*. The serial has to be resolved from the AVD name;
assuming `emulator-5554` targets somebody else's work.
3. **The debug build's application id carries a `.debug` suffix**, so
`monkey -p dev.privacyllc.period` aborts with *No activities found to run*
after a successful install — which reads as a broken app rather than a wrong
package name.
**And a free persistence result.** The emulator was killed and cold-booted with
onboarding half finished. The install survived, and the app restarted at the
beginning of onboarding with nothing partial committed — which is correct, and is
the first reboot evidence pass D has ever had.
**A guard failure of my own, from the entry above.** That entry named the bad
`forgejo-issue.py` path in backticks, and `doc-claims.sh` reads a backticked path
as a claim the file exists — so commit `ad085fb` shipped with the guard failing.
I had run it before writing the log entry and not after. The path is named bare
now, which is the convention `DOC_TRUST_MAP.md` already documents for
deliberately-absent files, and the guard reports 251 claimed paths all present.
- **Closed:** nothing. This closes no issue; it partly closes a standing QA gap,
which lives in `docs/qa/ClaudeQACoverage.md` rather than the tracker.
- **Next action:** Batch 06 — Privacy and Security, unchanged. File its issues
first; the milestone is empty. Biometric and PIN lock, export, irreversible
delete, the privacy promise in Settings, and making §45's logging rules true in
a release build.
- **Blockers:** #9, the Command Center webhook, still needs a person. Three of the
four QA gaps are unchanged and remain the cheaper spend: nobody has looked at a
real lock screen, TalkBack has never been run, and text has never been scaled.
The fourth — nothing has run at `minSdk` — is now partly answered, though
notifications, the instrumented suites and any multi-cycle history are still
unreached at API 26.
### 2026-08-18 — A doc audit against the code: 57 wrong claims in eighteen files ### 2026-08-18 — A doc audit against the code: 57 wrong claims in eighteen files
No code changed. Every document in the tree was checked against the source, the No code changed. Every document in the tree was checked against the source, the
@ -73,11 +132,13 @@ emptySet()` is pre-declared in the root build script, stricter than the sentence
it replaced, and matching no module until Batch 07 — which is exactly why the it replaced, and matching no module until Batch 07 — which is exactly why the
guard is proved by injection rather than trusted. guard is proved by injection rather than trusted.
**Smaller, and each a real trap.** `WORK_CYCLE.md` told the reader to run **Smaller, and each a real trap.** `WORK_CYCLE.md` told the reader to run a
`docs/architecture/scripts/forgejo-issue.py`, a template path that does not `forgejo-issue.py` under docs/architecture/scripts/ — a template path that does
exist here — missed by `doc-claims.sh` because it only reads backticked prose, not exist here, named bare above because `doc-claims.sh` reads a backticked path
not fenced blocks. `ClaudeReport.md`'s Round notes said "No rounds yet" after as a claim the file is present and this sentence says the opposite. The guard
three rounds, because `ClaudeQAPlan.md`'s after-a-round list never mentioned that missed it in `WORK_CYCLE.md` for the neighbouring reason: it reads backticked
prose, not fenced code blocks. `ClaudeReport.md`'s Round notes said "No rounds
yet" after three rounds, because `ClaudeQAPlan.md`'s after-a-round list never mentioned that
section; both are fixed, the playbook first. The instrumented-test count was section; both are fixed, the playbook first. The instrumented-test count was
eight in three places and is four. `HISTORY.md` said the repository had no code eight in three places and is four. `HISTORY.md` said the repository had no code
and that nothing had been tried and dropped, when three approaches had. The and that nothing had been tried and dropped, when three approaches had. The

View File

@ -155,10 +155,17 @@ existing is not a failure; it not existing while the gaps do is.
navigates a screen with their eyes shut. navigates a screen with their eyes shut.
- **Font scaling untried.** The hero number is 72sp; at the largest accessibility - **Font scaling untried.** The hero number is 72sp; at the largest accessibility
scale it may not fit beside anything. scale it may not fit beside anything.
- **No device at `minSdk`.** Everything so far ran on API 36. The two NewApi - **~~No device at `minSdk`~~ — partly closed on 2026-08-18 at `0d280d5`.** The
bugs above would have crashed on Android 8 through 13 and nothing except lint app has now been built, installed and driven on `PeriodMinSdk26`, an API 26
could see them. Lint is a good guard and it is not a substitute for one run on emulator: onboarding start to first forecast, the Material 3 date picker,
an old API level. Today, Calendar, Insights and Settings, plus a relaunch after the emulator was
killed and cold-booted. No crash, and no `NoSuchMethodError`, `VerifyError` or
`NoClassDefFoundError` attributable to the app — the only `NoClassDefFoundError`
in logcat belongs to `com.google.android.googlequicksearchbox`. What is still
unreached at API 26: notifications and their lock-screen rendering, the
instrumented suites, and anything needing more than one cycle of history, so
fertility never became visible. Passes C, E, F, G and H have never run at this
API level.
- **Physical-device coverage is undecided.** Passes F and G need a real device - **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 with a lock screen and TalkBack; which device that is has not been chosen, and
an emulator is not a substitute for either. an emulator is not a substitute for either.

View File

@ -24,6 +24,44 @@ Review trigger: Any new user-facing surface, or a defect class that got through
- Seed the cycle history deliberately. A round run against three cycles and a - Seed the cycle history deliberately. A round run against three cycles and a
round run against twelve are not comparable, and the forecast is the product. round run against twelve are not comparable, and the forecast is the product.
### The emulators, by name
Rounds cite these by name, so they are defined here rather than in the round
that happened to use one. Both are Pixel 6 profiles, x86_64, no Play Store.
| AVD | API | Why it exists |
| --- | --- | --- |
| `PeriodQA` | 36 | The everyday round. Current Android, and what Rounds 13 ran on. |
| `PeriodMinSdk26` | 26 | **`minSdk`.** The floor the app claims to support, and the level lint's `NewApi` findings are about — two of which were real crashes on every device below Android 14, invisible to the unit tests and to an API 36 emulator. |
Recreate either with:
```bash
SDK=$ANDROID_HOME
$SDK/cmdline-tools/latest/bin/sdkmanager --install "system-images;android-26;google_apis;x86_64"
$SDK/cmdline-tools/latest/bin/avdmanager create avd \
-n PeriodMinSdk26 -k "system-images;android-26;google_apis;x86_64" -d pixel_6
```
**Launch headless, and target it by serial.** Other projects on this machine keep
their own emulators running, so `adb` regularly has three devices attached and a
bare `adb shell` fails with *more than one device/emulator*. Resolve the serial
by AVD name rather than assuming `emulator-5554`:
```bash
$SDK/emulator/emulator -avd PeriodMinSdk26 -no-window -no-audio -no-boot-anim &
D=$(adb devices | awk '/emulator-/{print $1}' \
| while read d; do [ "$(adb -s $d emu avd name | head -1 | tr -d '\r')" \
= PeriodMinSdk26 ] && echo $d; done)
adb -s "$D" install -r app/build/outputs/apk/debug/app-debug.apk
```
`-no-window` is not only for headless convenience: a windowed emulator dies with
`XIO: fatal IO error on X server ":0"` if the launching session's display goes
away, which killed one mid-round. And the debug build's application id carries a
`.debug` suffix, so it is `dev.privacyllc.period.debug` that must be launched —
`monkey -p dev.privacyllc.period` aborts with *No activities found to run*.
## The passes ## The passes
Each pass gets a letter, so `ClaudeQACoverage.md` can report per pass and a Each pass gets a letter, so `ClaudeQACoverage.md` can report per pass and a