docs: log Batch 06 closing, four new guards, and an emulator that was not mine

Step 6 of WORK_CYCLE. Next action is filing Batch 07's issues: Monetization is
an open milestone with zero issues, so the largest remaining body of code is
described nowhere and is missing from the denominator the project screen
reports.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
null 2026-08-19 22:17:42 -05:00
parent 6591951c8d
commit 25102269d9
1 changed files with 89 additions and 0 deletions

View File

@ -32,6 +32,95 @@ written and stay true. It is exempt from review for the same reason a receipt is
## Entries
### 2026-08-19 (later) — Batch 06 closed, and four guards that can now fail
Export, the privacy promise, the themed-drawable guard and the notification icon
all landed, and **Batch 06 is closed** with seven issues in it. The tracker has
three issues left and none of them is code: two need artwork, and the third
(#31, the incognito launcher) says in its own body that the alias must land with
§32's toggle rather than ahead of it.
**#35, export.** One pretty-printed JSON file, plaintext, written straight into
a document the user picked through the Storage Access Framework. No copy in
`cacheDir`, no `FileProvider`, no path handed to another app — and a test walks
`cacheDir` afterwards and requires it empty, which fails the moment anybody
reintroduces write-then-share.
The format is pinned **byte-for-byte** against a committed golden file, because
the issue is right that it outlives the batch: whatever ships first is what
people's archives are in. It also renders identically in UTC, +14 and 12,
because `Converters.kt` stores a `LocalDate` as its epoch day precisely so it
"cannot carry a timezone by accident" and a zone-aware formatter here would be
off by one day for everybody east or west of whoever wrote it.
**Plaintext was the decision, and it reverses an earlier note in this file.**
The entry above records the next action as "export with the file encrypted".
That is wrong, and `SECURITY.md` now says why: the export is the copy that makes
a lost Keystore key survivable rather than final — the exact condition written
down for ever revisiting database encryption. A passphrase would reproduce the
failure that decision avoided, and nothing, including this app, could open the
result.
"Only the user's own data" is enforced structurally rather than by review:
`:core:export` is pure JVM and depends on `:domain:cycle` alone, so `Prediction`,
`FertilityEstimate` and `android.os.Build` are not on its classpath. Adding a
forecast does not compile.
**Four guards exist now that did not, and each was watched failing.**
- `checkThemedDrawables` (#42) — every drawable has a night twin, both
directions. `prove-guard.sh` cannot drive it, because it replaces a string
in a file and this guard's failure mode is a file that is *not there*, in a
set that is all `.webp`. GUARDS.md §9 records that class.
- `checkNoSharedStorageWrites` — §45's shared-storage ban was enforced by
nobody having typed it. `checkPermissions` structurally cannot see it: it
matches `<uses-permission>`, and a `<provider>` declaring `FileProvider`
merges green.
- `checkNoHealthLogging` completeness — a module missing from
`modulesSeeingHealthData` was silently exempt with a green build. Both
`:core:security` and `:core:export` would have gone through that hole.
- `checkPermissions` narrowed — it was reading `merged_manifest/debugUnitTest`
and failed on `REORDER_TASKS`, a test-runner permission that does not ship.
The tempting fix was to allowlist it, which would have permitted it in the
real manifest too and quietly undone the guard.
**Three defects found that nobody was looking for.**
1. `NotificationPrivacyTest` could never run on `PeriodMinSdk26`.
`GrantPermissionRule` asked for `POST_NOTIFICATIONS` unconditionally, and
that permission arrived in API 33 — so every test in the class errored
before its first assertion, on the one emulator where it mattered. The class
guards what a locked screen shows.
2. No Robolectric test in `:app` could read a string resource.
`unitTests.isIncludeAndroidResources` was never set there, so the module
owning nearly all the user-facing copy was the one whose copy could not be
tested.
3. `prove-guard.sh`, a third time: a `PROVE_GUARD_FAIL_PATTERN` matching
**nothing** gives the same "caught it, and only it" verdict as one matching
once. The empty `--- what failed ---` block is the tell, and GUARDS.md §8
now says to read it.
**A mistake worth recording.** Looking for a device to verify the export on, I
installed the debug APK onto whatever `adb` reported and drove it — and it was
the **FruitFall** emulator, running another project's app. My package is
uninstalled and their app was left running, but the lesson is that a device on
`adb devices` is not evidence of a device that is mine. The export's on-device
checks are three new rows in `SECURITY_CHECKLIST.md` rather than a claim.
**What this proved:** 261 JVM tests, none skipped. Five guards green, four of
them proved able to fail today.
**Next action:** file Batch 07's issues. Monetization is an open milestone with
**zero issues**, closed or open, so the largest remaining body of code in this
project is described nowhere — which also means the 84% figure on the project
screen is measured against a denominator that excludes it. Ads and billing need
scoping before they need writing, and §34's advertising boundary is the part
that most needs stating before anybody starts.
**Blockers:** none for code. #31 waits on §32's toggle by its own instruction,
#32 and #44 need artwork, and the public privacy page — which #37's Privacy
Policy row waits on — is site-side work no agent here can do.
### 2026-08-19 — The app lock, and three defects only testing found
#34 is closed. The app asks for a PIN before it opens, and a forgotten PIN