docs: record the two release-blockers and the reported trap
Development log entry per WORK_CYCLE step 6, covering what the three explorations found beyond the bug that was reported -- including that ExportHost has almost certainly never been visible to anyone, and that the app-lock entry's "setting a PIN locked you out" fix closed the common ordering but not the race. Next action and blockers named at the end, as the file's own rules ask. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ff3cbe89ad
commit
7447156cbc
|
|
@ -32,6 +32,90 @@ written and stay true. It is exempt from review for the same reason a receipt is
|
|||
|
||||
## Entries
|
||||
|
||||
### 2026-08-20 (evening) — Two release-blockers, and the trap that was reported
|
||||
|
||||
Kaspa reported one bug — *"on the settings page, if you go to app lock you can't
|
||||
unnavigate out of it"* — and asked for biometrics-or-PIN, back arrows, unified
|
||||
code, error handling, notifications, and blind spots covered. Three read-only
|
||||
explorations of settings navigation, the app lock, and the reminder pipeline
|
||||
turned up considerably more than the report, including two release-blockers
|
||||
nobody had noticed.
|
||||
|
||||
**Batch 12 · P0 — "Ended" recorded the start of a period.** The period-end
|
||||
check-in asks *"Is your period over?"* and offers **Ended** / **Still going**.
|
||||
`ReminderWorker` attached writes to buttons *by position* — index 0 to "started",
|
||||
index 1 to "not yet" — for every kind of reminder, so "Ended" inserted a new
|
||||
period starting today, in the middle of the one it was asking about, and "Still
|
||||
going" filed a censoring observation against a forecast that had already
|
||||
arrived. It corrupted the health record and every forecast built on it. The
|
||||
handler had no test at all, which is how it survived; it owns the only two writes
|
||||
reachable from a locked phone. A button is now one thing carrying both halves,
|
||||
`ENDED` closes the period through `setPeriodEnd`, and `STILL_GOING` deliberately
|
||||
writes nothing.
|
||||
|
||||
**Batch 11 · P0 — Change PIN never checked the current PIN.** The screen fired
|
||||
the check and advanced to "Choose a PIN" in the same breath, without waiting for
|
||||
the answer. Any four digits reached the replacement screen and overwrote the PIN,
|
||||
so anyone holding the unlocked phone could lock the owner out of her own history
|
||||
permanently — under the no-recovery policy the only way back is to erase
|
||||
everything. Fixed at both altitudes: the step advances inside the verified
|
||||
callback, and the ViewModel refuses a replacement no successful check authorised.
|
||||
|
||||
**The reported trap had three causes, all real.** The bottom nav saved and
|
||||
restored per-tab stacks, so tapping Settings from App lock restored
|
||||
`[settings, settings/lock]` and landed back on App lock — and the saved stack
|
||||
survived visiting other tabs, pinning the Settings tab there for the rest of the
|
||||
process. No screen in the app had a back arrow at all: a grep for `TopAppBar`,
|
||||
`navigationIcon`, `BackHandler` and `popBackStack` across `app/`, `core/` and
|
||||
`domain/` returned nothing, and App lock had a headline styled like a bar without
|
||||
being one. And setting a PIN could still lose the race the log recorded as fixed
|
||||
in the app-lock entry: the fix lived in the scope the race destroys.
|
||||
|
||||
Settings is now a nested graph, leaving it never saves its stack, re-tapping a
|
||||
tab returns it to its root, and one `SettingsSubpage` carries the bar for all
|
||||
three children. App lock's steps are remembered state rather than destinations,
|
||||
so its back is step-aware and the arrow and the gesture agree. `setPin` unlocks
|
||||
before it writes, which makes the bad pair unobservable.
|
||||
|
||||
**`ExportHost` has almost certainly never been visible.** It was emitted as a
|
||||
sibling *before* the `Scaffold`, inside a `Box`, where later siblings draw on top
|
||||
— and the Scaffold paints an opaque container. It is the Scaffold's top bar now.
|
||||
The export's on-device checks were never run either, which is why nobody noticed.
|
||||
|
||||
**The lock now fails towards opening.** `hasPin` had no `catch` and is collected
|
||||
on the startup path in two places that cannot handle a throw, so a corrupt
|
||||
`app_lock` file crashed the app before any UI existed. It reads as *no lock* now,
|
||||
and the store has a corruption handler — without one, a single bad write leaves
|
||||
the lock unable to be set *and* the "Forgot your PIN?" erase unable to run.
|
||||
Separately, a wrong PIN whose counter could not be signed used to cost nothing;
|
||||
the unwritten counter is held in memory and read back when it is the longer wait.
|
||||
|
||||
**And the app can now stop asking.** §30's stopping rule was unreachable: the
|
||||
check-in count was reset by a flow whose first emission always passes, in a
|
||||
coordinator started in every process — including the one WorkManager spawns for
|
||||
the reminder, moments before it reads the count. The count now carries the row id
|
||||
of the period it was asked about, so a new period starts it over by arithmetic
|
||||
and the reset chain is deleted.
|
||||
|
||||
Compose UI tests run on the JVM under Robolectric now; nothing in this project
|
||||
could assert a navigation behaviour before. Every fix was proved with
|
||||
`scripts/prove-guard.sh` — one red each, except removing the change-PIN
|
||||
write-site guard, which reddens three because that is the whole of its surface.
|
||||
|
||||
**16 issues filed** across two new milestones: `Batch 11 — Settings and the App
|
||||
Lock` (#60-#67) and `Batch 12 — Reminders that tell the truth` (#68-#75). Five
|
||||
are closed.
|
||||
|
||||
**Next action:** #71 — a denied notification permission is ignored, the toggle
|
||||
stays on, and the check-in counter still advances on reminders that were never
|
||||
posted, so the app can "stop asking" having never asked once. Then #72, where
|
||||
changing the reminder time does not move it (verified against WorkManager's
|
||||
bytecode: `UPDATE` keeps the old `lastEnqueueTime`).
|
||||
|
||||
**Blockers:** none. Note that a flaky 5-second test budget was raised to 30s
|
||||
after one red in a parallel run — each PIN in that test costs a real
|
||||
210,000-iteration derivation.
|
||||
|
||||
### 2026-08-20 (later) — Batch 09: the learning loop, measured
|
||||
|
||||
Started from a question — *how well does this app learn a period?* — and
|
||||
|
|
|
|||
Loading…
Reference in New Issue