mirror of
https://github.com/kuhyx/wake-alarm.git
synced 2026-11-11 18:14:04 +01:00
Record the phone alarm done; drop the finished session file
Items 1 and 4 ship and are emulator-verified. Item 3 has NFC and the steps+elapsed fallback wired into the ring screen; the QR scanner UI is the remaining piece and is now the top entry under "Remaining". Records what has NOT been verified rather than letting the deploy stand in for it: the ring has only ever fired on the API 36 AVD, never on the Pixel, and never with DND on — which is the one condition the STREAM_ALARM claim rests on. Four new traps, all of which cost time here: the plan's LogStore.changes does not exist (so re-arm needs three explicit hooks), the Android SDK was never missing but merely unexported, a manifest permission is not a granted permission, and a debug probe with its own UI proves nothing about the route it was written to test. TODO-phone-alarm-session.md deleted: its work is done, and a finished task file must not be left behind. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EckWmyfetYq3Mi8MAnUudh
This commit is contained in:
parent
cb50e58a56
commit
9980b47cf4
@ -20,16 +20,30 @@ starting; this file deliberately does not duplicate it.
|
||||
| 2 power button | **Done.** `_inhibit.py` (logind block, self-terminating, stale cleanup by pid via `/proc`) + `_alarm_session.py` fire-time record, re-fired by `_trigger` outside the catch-up window. |
|
||||
| 10 vmbox | **Done.** `scripts/vmbox/harness.sh` + `DOCS-vmbox.md`. 8 cases, fails closed. Found 2 live bugs the host could not reach. |
|
||||
| 5 curtains | **Done.** `_curtains.py` driver protocol + no-op default, called from `prepare_hardware()`. `DOCS-curtains-hardware.md` has the buy list. |
|
||||
| 1/4 phone alarm | **Done.** Five Kotlin files behind one MethodChannel + the Dart ring screen. The phone arms and rings its own alarm; no Clock-app confirmation. Verified on the API 36 AVD (screen Asleep→Awake, AlarmActivity over the lockscreen) and deployed to the Pixel 6a. |
|
||||
| 3 room proofs | **Mostly done.** NFC (hand-written `NfcReader.kt`) and the steps+elapsed fallback ship and are wired into the ring screen. QR is specified and unit-tested (`checkQr`) but has **no scanner UI** — `mobile_scanner` was left as the droppable extra. |
|
||||
|
||||
## Remaining, in order
|
||||
|
||||
1. **Items 4/3/1, phone.** Biggest chunk by far. Own session. Everything here
|
||||
is new — the app has zero alarm capability today, only a time picker.
|
||||
2. **Item 8, mutual gating.** Needs 1 done first. This is the phase that
|
||||
legitimately touches `_sync.py`, so it carries that split (see below).
|
||||
1. **Item 3 leftovers.** The QR scanner UI (`mobile_scanner`) and a Settings
|
||||
screen to register the NFC tag id / QR payload. `RoomProofSettings` already
|
||||
persists both and `RoomProof.checkQr` is unit-tested — what is missing is
|
||||
only the UI that feeds them. NFC and the steps fallback already work, so
|
||||
the alarm is dismissable without this.
|
||||
2. **Item 8, mutual gating.** Phone half is now in place. This is the phase
|
||||
that legitimately touches `_sync.py`, so it carries that split (see below).
|
||||
3. **Item 13, black screen.** Different repo (`~/testsAndMisc/
|
||||
linux_configuration`), host-only, blocks nothing.
|
||||
|
||||
### Not yet verified on the phone
|
||||
|
||||
The ring itself has only been fired on the API 36 AVD. On the Pixel 6a the
|
||||
build installs and the runtime permission prompts appear, but **an actual
|
||||
alarm has never rung on it** — that needs an alarm armed through the picker
|
||||
(which needs a sync backend connected) and a wait to the fire instant. Ring
|
||||
with **DND on**, since "STREAM_ALARM ignores DND" is the load-bearing claim
|
||||
and has not been observed on hardware.
|
||||
|
||||
## Decisions already made — do not re-litigate
|
||||
|
||||
- Failsafe stops **sound only**; lock, challenge and workout gate persist.
|
||||
@ -49,6 +63,24 @@ starting; this file deliberately does not duplicate it.
|
||||
- Curtains: no hardware; pluggable driver + logging no-op default.
|
||||
- Quality bar: 100% branch coverage, no new `# noqa` / `type: ignore`.
|
||||
|
||||
## Traps found in the phone session
|
||||
|
||||
- **The plan's `LogStore.changes` does not exist.** `crdt_sync_dart` has no
|
||||
Stream at all. Re-arm therefore needs three explicit hooks — startup,
|
||||
BOOT_COMPLETED, dismiss — and dropping any one makes the alarm ring once
|
||||
and then silently stop forever.
|
||||
- **The Android SDK was never missing, only unexported.** `~/Android/Sdk` has
|
||||
`cmdline-tools`, `emulator` and `platform-tools`; only `adb` was on PATH.
|
||||
Export `ANDROID_HOME` rather than installing anything.
|
||||
- **A permission declared in the manifest is not a permission granted.**
|
||||
Both `POST_NOTIFICATIONS` and `ACTIVITY_RECOGNITION` are runtime grants, and
|
||||
each one missing silently breaks a different half of the dismiss path
|
||||
(no full-screen intent / no step sensor). The AVD caught both because a
|
||||
freshly installed app there sits at `importance=NONE`.
|
||||
- **A debug probe entrypoint with its own UI proves nothing about the route.**
|
||||
The first probe rendered its own widget, so the AVD run validated the Kotlin
|
||||
and nothing about `/ring`. Point the probe at the real app widget.
|
||||
|
||||
## Traps that cost time this session
|
||||
|
||||
- **The 250-line gate fires on *staged* files.** Touching a file means
|
||||
|
||||
@ -1,96 +0,0 @@
|
||||
# Next session: make the phone a real alarm (items 4, 3, 1)
|
||||
|
||||
REMOVE ME AFTER FINISH
|
||||
|
||||
Paste the block at the bottom into a fresh session. Everything above it is
|
||||
context for *you*, the human, deciding whether the framing is right.
|
||||
|
||||
## Why this is its own session
|
||||
|
||||
The PC half is done (phases 0–7, `4286fcb`..`c131923`). What is left is
|
||||
almost entirely new Kotlin + Dart on a bleeding-edge Android toolchain, needs
|
||||
an emulator that is not installed yet, and ends in physical-device
|
||||
verification. The plan calls it "biggest chunk by far. Own session."
|
||||
|
||||
## State of the phone app, measured 2026-08-27 (not guessed)
|
||||
|
||||
- `phone_app/lib/` has 3 screens and 7 services. **Zero alarm capability** —
|
||||
it is a time picker that syncs a value.
|
||||
- `MainActivity.kt` exists at
|
||||
`android/app/src/main/kotlin/com/kuhy/wake_alarm_sync/MainActivity.kt` and
|
||||
is bare. `applicationId = "com.kuhy.wake_alarm_sync"`.
|
||||
- **`minSdk`/`targetSdk`/`compileSdk` are all delegated** to the Flutter
|
||||
Gradle plugin (`flutter.minSdkVersion` etc.) — there is no floor, and
|
||||
full-screen-intent and foreground-service semantics cannot be reasoned
|
||||
about without one. Pin them first.
|
||||
- **Only `/usr/bin/adb` is on PATH.** No `emulator`, no `avdmanager`, no
|
||||
`sdkmanager`. The emulator work in the plan needs these installed first,
|
||||
and the emulator image must be **API 36** to match the phone — the API 34
|
||||
image validates against semantics the target device does not have.
|
||||
|
||||
## The prompt
|
||||
|
||||
```
|
||||
Implement items 4, 3 and 1 from TODO-hardening-continue.md in ~/wake-alarm —
|
||||
the phone half of the wake alarm.
|
||||
|
||||
Read first, in this order:
|
||||
1. TODO-hardening-continue.md (state, decisions, traps)
|
||||
2. ~/.claude/plans/wake-alarm-still-does-witty-toucan.md, phases 4 and 5
|
||||
(the full design, with every measured finding)
|
||||
3. CLAUDE.md (repo invariants)
|
||||
|
||||
Scope, in order:
|
||||
|
||||
1. Prerequisites, before any feature work:
|
||||
- Pin explicit minSdk/targetSdk/compileSdk in
|
||||
phone_app/android/app/build.gradle.kts. They are all delegated to the
|
||||
Flutter plugin today, so there is no floor to reason about.
|
||||
- Install emulator/avdmanager/sdkmanager (only adb is on PATH) and create
|
||||
an API 36 AVD. API 34 validates against semantics the phone lacks.
|
||||
|
||||
2. Item 4 + 1 — the phone actually rings. Hand-written Kotlin behind ONE
|
||||
MethodChannel on the existing bare MainActivity.kt, never a plugin: the
|
||||
stack is AGP 9.0.1 / Gradle 9.1 / Kotlin 2.3.20, and a plugin that will
|
||||
not compile breaks the build outright rather than degrading. Five files:
|
||||
AlarmScheduler, AlarmService, AlarmActivity, BootReceiver, AlarmChannel.
|
||||
AlarmManager.setAlarmClock(), a foreground service with a wakelock,
|
||||
STREAM_ALARM (load-bearing — it is what ignores DND and silent mode, never
|
||||
STREAM_MUSIC), a full-screen intent over the lockscreen, re-arm on
|
||||
BOOT_COMPLETED *and* on dismiss.
|
||||
Dart side: main.dart has no init seam today — it needs
|
||||
WidgetsFlutterBinding.ensureInitialized(), a navigatorKey and a route.
|
||||
Port the five challenge kinds from _challenges.py so both halves behave
|
||||
identically.
|
||||
|
||||
3. Item 3 — separate-room proofs. NFC or QR qualifies; steps alone does NOT
|
||||
(the phone is stationary while you walk to it, so steps only accrue after
|
||||
pickup and can be faked by shaking it in bed). Steps + elapsed time is the
|
||||
fallback when both others fail. This is a decision already taken — do not
|
||||
re-litigate it.
|
||||
|
||||
Constraints:
|
||||
- 100% branch coverage, no new # noqa / type: ignore.
|
||||
- The 250-line gate fires on STAGED files. Do not edit an oversized file this
|
||||
change does not already have to touch — that cascade cost a full revert
|
||||
last session. A new constant goes in its single consumer.
|
||||
- Verify on the PHONE (23181JEGR08034) via the phone-deploy skill, not just
|
||||
the emulator. A companion-app change is not done on desktop.
|
||||
- Do not touch the PC half's invariants: no [Install] on wake-alarm.service,
|
||||
_state.py stays UTC-keyed (screen_locker mirrors that helper), the trigger
|
||||
starts morning-routine.service.
|
||||
|
||||
Item 8 (mutual gating) depends on this and is the session after. Item 13
|
||||
(black screen) is a different repo, host-only, and blocks nothing.
|
||||
```
|
||||
|
||||
## After that session
|
||||
|
||||
- **Item 8, mutual gating.** Phone challenge first, then PC, with a bounded
|
||||
escape: if sync is unreachable ~600 s the PC gate degrades to
|
||||
locally-solvable and logs a WARNING. Without it, a Firebase outage means you
|
||||
can neither dismiss nor get to work. This is the phase that legitimately
|
||||
touches `_sync.py`, so it carries that file's 371-line split.
|
||||
- **Item 13, black screen.** `~/testsAndMisc/linux_configuration` plus /boot
|
||||
and /etc. Host-only — vmbox is BIOS/GRUB and this machine is UEFI, so a
|
||||
guest pass there is actively misleading.
|
||||
Loading…
Reference in New Issue
Block a user