mirror of
https://github.com/kuhyx/wake-alarm.git
synced 2026-11-10 19:27:02 +01:00
Move TODOs to todo app
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
58c4260b6e
commit
5525ca6f95
@ -1,23 +0,0 @@
|
||||
# Wake-alarm hardening — residue
|
||||
|
||||
REMOVE ME AFTER FINISH
|
||||
|
||||
Phases 0-7 shipped. The PC alarm window, gate and `_alarm.py` were deleted in
|
||||
`e67cef0` (2026-08-30), so the old "item 8 mutual gating" (PC half) no longer
|
||||
has anything to gate and was dropped. Context: `DOCS-wake-enforcement.md`.
|
||||
|
||||
## Remaining
|
||||
|
||||
1. **Item 13, black screen.** Host-only, other repo
|
||||
(`~/src/testsAndMisc/linux_configuration`). The original plan file
|
||||
(`~/.claude/plans/wake-alarm-still-does-witty-toucan.md`) is gone, so the
|
||||
symptom is no longer written down: needs kuhy to restate it, or drop it.
|
||||
2. **Two-tag dismiss sequence, end to end on the Pixel.** Needs kuhy: a second
|
||||
physical NFC tag (only one is registered, so every on-device run fell
|
||||
through to the 5-minute bypass).
|
||||
3. **Locked-keyguard dismiss, run to completion.** Needs kuhy: a run with a
|
||||
locked phone (PIN-prompt path is implemented but unproven).
|
||||
|
||||
## done
|
||||
|
||||
Items 1-3 each confirmed on the phone or dropped by kuhy; then delete this file.
|
||||
@ -1,63 +0,0 @@
|
||||
# Hardware ideas: defeat a fully-powered-off phone
|
||||
|
||||
REMOVE ME AFTER FINISH
|
||||
|
||||
Both items below were scoped during the 2026-09-23 workday-stick session
|
||||
(`DOCS-morning-session-pc.md`) and explicitly deferred out of scope for that
|
||||
session: no existing firmware project and no owned wearable, both need a real
|
||||
purchase before any code gets written. Written here instead of
|
||||
`~/src/todo/BACKLOG.md` because that file is gitignored/export-only from the
|
||||
todo app and a manual edit there is silently lost on the next export — this
|
||||
is the durable copy.
|
||||
|
||||
## Item 1 — second wake-alarm device: rings independently of the phone's power state
|
||||
|
||||
### what
|
||||
An ESP32 + buzzer that polls the same Firebase alarm/session record
|
||||
wake-alarm already reads and keeps ringing until it sees a dismiss or a
|
||||
started session — so powering the phone off can no longer silently kill the
|
||||
whole wake attempt the way it does today.
|
||||
|
||||
### where
|
||||
new repo (no existing ESP32/PlatformIO/Arduino project anywhere in `~/src`
|
||||
to build on — checked). Firebase-only, per the "new syncing apps are
|
||||
Firebase-only" rule.
|
||||
|
||||
### must
|
||||
- must not require the phone to relay anything — the whole point is
|
||||
surviving the phone being off.
|
||||
- optional: a physical snooze/dismiss button, since the phone's own dismiss
|
||||
gate isn't reachable from the device.
|
||||
|
||||
### done
|
||||
On a Tue/Wed/Thu, power the phone off right at the alarm and the ESP32
|
||||
still rings, and keeps ringing, until the phone's session shows a start.
|
||||
|
||||
### verify
|
||||
Bench test first (USB power, no case), then a real overnight run.
|
||||
|
||||
### read first
|
||||
- `DOCS-phone-alarm.md` (the Firebase alarm schema to poll)
|
||||
- `DOCS-morning-session-pc.md` (why this exists: the workday-stick project,
|
||||
2026-09-23)
|
||||
|
||||
## Item 2 — wearable wake-defeat check
|
||||
|
||||
### what
|
||||
A cheap fitness band whose own sleep log (stored on the band, synced later)
|
||||
can show "asleep again after the alarm rang" even when the phone was fully
|
||||
off and missed the whole thing.
|
||||
|
||||
### where
|
||||
new app or an extension of `phone_app/`, once a device is picked.
|
||||
|
||||
### must
|
||||
- must not: buy or integrate anything before checking Gadgetbridge's
|
||||
current supported-device list — research first, kuhy owns no wearable and
|
||||
none is integrated anywhere in `~/src` today (checked).
|
||||
|
||||
### done
|
||||
Not yet scoped — picking a supported device is the blocking step.
|
||||
|
||||
### verify
|
||||
Blank — desktop, once there's something to verify.
|
||||
@ -1,90 +0,0 @@
|
||||
# Wake enforcement v2 — follow-ups for the next session
|
||||
|
||||
REMOVE ME AFTER FINISH
|
||||
|
||||
Handoff from the 2026-10-01 session that built v2. Read
|
||||
`DOCS-wake-enforcement.md` (the contract + verification status) and
|
||||
`AGENTS.md` first. Start **after the morning of 2026-10-02** so there is real
|
||||
evidence to check.
|
||||
|
||||
## Prompt to paste into the new session
|
||||
|
||||
> Work through ~/src/wake-alarm/TODO-wake-enforcement-v2-followups.md top to
|
||||
> bottom. Verify each item yourself on the real target (phone 23181JEGR08034,
|
||||
> this PC) before calling it done, then delete the TODO file in the final
|
||||
> commit.
|
||||
|
||||
## 1. Check the first real morning (2026-10-02, Fri, carrot day)
|
||||
|
||||
- `python -m wake_alarm._audit 2026-10-02` (and the 11:05 file in
|
||||
`~/data/wake-alarm/incidents/`). Expected: phone `alarm_fired`
|
||||
(`reason=daily`), `ring_started_published` with `source=alarm` (headless
|
||||
engine, release build — never verified in release before) or
|
||||
`ring_screen`, `stick_check` verdict for a non-stick day, no fallback ring
|
||||
unless the phone really was silent.
|
||||
- `adb logcat` for `WakeAlarmHeadless` / `WakeAlarmStick*` if anything is
|
||||
missing. Firebase fields `ring_started`, `phone_events` must be non-empty
|
||||
(`enforcement_view(fetch_firebase_logs())`).
|
||||
- Any MISMATCH in the report is a bug to fix, not a note.
|
||||
|
||||
## 2. focus-owner reacting to a real lockdown (needs kuhy's OK + timing)
|
||||
|
||||
Never verified on the Pixel. Either wait for the first real Tue/Wed/Thu miss
|
||||
and read focus-owner's Debug log (`LOCKDOWN_ACTIVATED` with the through-date,
|
||||
then a `WORKDAY_LOCKDOWN` pass), or — with kuhy's explicit OK, at a time he
|
||||
names — trigger one and lift it with the escape button (logs an escape).
|
||||
|
||||
## 3. Root ring vs a card held busy by PipeWire
|
||||
|
||||
Host test 2026-10-01: `wake-alarm-ring-alsa.service` got `Device or resource
|
||||
busy` while the user's PipeWire held card `Generic_1` (ALC897). kuhy approved
|
||||
killing the holder (`fuser -k -TERM /dev/snd/pcmC<N>D0p` after 3 busy opens,
|
||||
in `scripts/pc/ring_alsa.sh`), but the auto-mode classifier refused the edit
|
||||
twice. **kuhy must add a permission rule first**, or choose the non-killing
|
||||
alternative: when the root unit cannot open the card, `_ring_player` falls
|
||||
back to `PipeWireBackend` (PipeWire holds the card, so it can mix the ring
|
||||
in). Add a vmbox case that holds `snd-dummy` open and proves the ring still
|
||||
plays.
|
||||
|
||||
## 4. S5 (poweroff) RTC wake test on the host
|
||||
|
||||
Hibernate (S4) wake is verified and is the default mode. S5 is not. The
|
||||
self-resume boot unit (`~/data/claude-scratch/wake-fw/s5_after_boot.sh`,
|
||||
never installed) was refused by the classifier. Simplest: with kuhy present,
|
||||
`sudo -n rtcwake -m no -s 240` + record the epoch, `systemctl poweroff`; kuhy
|
||||
powers on if it does not wake; then compare `uptime -s` with the epoch. If S5
|
||||
works, decide with kuhy whether to switch `pc_wake.json` to `poweroff` (covers
|
||||
a typed `shutdown`, not just the power key).
|
||||
|
||||
## 5. utils dependency upgrade — prepared, uncommitted
|
||||
|
||||
In `~/src/utils` (do NOT stage the other session's `ts_config/README.md`,
|
||||
`scripts/lib/alias-imports.mjs`, `test/alias-imports.test.js`): pylint 4.1.1
|
||||
×3, typescript-eslint 8.71.0 (ts_config/ts_core/web_ui), @eslint-react
|
||||
5.23.3, eslint-plugin-sonarjs 4.2.2, pnpm 12.8.1 (+ lockfiles,
|
||||
`ts_config/pnpm-workspace.yaml`), allowlist: typescript-eslint entry removed,
|
||||
typescript entry now `blocked_by: transitive:typescript-eslint@8.71.0`.
|
||||
Lint + tests pass in all three. `finish_auto.sh` fails at its test step with
|
||||
corepack `MODULE_NOT_FOUND .../pnpm/12.8.1/bin/pnpm.cjs` (pnpm 12.8 ships a
|
||||
native binary; corepack 0.36 can't launch it). Global pnpm 12.8.1 is now
|
||||
installed directly (`corepack disable pnpm`); find what in the gate still
|
||||
calls corepack (read `~/data/claude-scratch/wake-v2/utils-commit.log`), fix,
|
||||
commit, watch CI green.
|
||||
|
||||
## 6. Fleet repos still pinning typescript-eslint 8.70.0
|
||||
|
||||
The allowlist entry that excused them is gone, so their freshness gate turns
|
||||
red on the next run: konbini-67, iron-and-anvil, awesome-mcp-explorer,
|
||||
europe-county-map and any other repo pinning it (`grep -rl
|
||||
'"typescript-eslint": "8.70.0"' ~/src --include=package.json`). Bump each to
|
||||
8.71.0, lint + test, commit, CI green.
|
||||
|
||||
## 7. Small leftovers
|
||||
|
||||
- `~/src/todo`: `cupertino_icons` 1.0.9 → 2.0.0 — another session had
|
||||
uncommitted work there; only bump once its tree is clean.
|
||||
- Global `~/.claude/CLAUDE.md` sandbox-first note says the vmbox guest is
|
||||
BIOS-only; vmbox has `--uefi` and `vm wake` (RTC-wake emulation) since
|
||||
utils 694c013. Ask kuhy before editing that file.
|
||||
- Clear the test data left in the `wake_alarm_api36` emulator (escape for
|
||||
10-01, ringStarted, stick state) before it is ever signed into sync.
|
||||
@ -1,202 +0,0 @@
|
||||
> **This file is a ready-to-use prompt.** Open this repo and say
|
||||
> "do dopamine-ux-wake-alarm". It is self-contained -- it needs no context from
|
||||
> any other session.
|
||||
>
|
||||
> Canonical copy: `~/src/utils/dopamine-ux/09-wake-alarm.md` (source of truth; this is a
|
||||
> distributed copy). Running order and cross-prompt rules: `~/src/utils/dopamine-ux/00-INDEX.md`.
|
||||
> Generated 2026-08-16 from a survey of this repo -- line numbers are accurate
|
||||
> as of that date; **anchor on the symbol names, not the line numbers.**
|
||||
|
||||
## ⛔ WHEN YOU FINISH THIS PROMPT
|
||||
|
||||
Do **both**, in the same session that completes the work:
|
||||
|
||||
1. **Delete this file** and commit that deletion together with the
|
||||
implementation:
|
||||
```bash
|
||||
git rm prompts/dopamine-ux-wake-alarm.md
|
||||
```
|
||||
Do not leave a finished prompt lying in the repo -- a stale prompt is
|
||||
indistinguishable from a pending one, and the next session will re-run it.
|
||||
|
||||
2. **Log completion in the canonical index**, in `~/src/utils` (a *different* repo,
|
||||
so it needs its own commit):
|
||||
```bash
|
||||
cd ~/src/utils
|
||||
# append to the "Completion log" table in dopamine-ux/00-INDEX.md:
|
||||
# | 09-wake-alarm.md | DONE <YYYY-MM-DD> | <impl commit sha> | <one-line note> |
|
||||
git add dopamine-ux/00-INDEX.md
|
||||
git commit -m "dopamine-ux: mark 09-wake-alarm.md done"
|
||||
git push
|
||||
```
|
||||
|
||||
If you finish only part of the prompt, do **neither** -- instead edit this file
|
||||
to strike out what is done and state what remains, so the next session starts
|
||||
from the true state. Partial completion must never be recorded as DONE.
|
||||
|
||||
---
|
||||
|
||||
Add motion and haptics to the wake-alarm phone app
|
||||
|
||||
## findings (2, ranked by value over effort)
|
||||
|
||||
1. **Setting an alarm gives no confirmation or haptic** — and since the real
|
||||
alarm lives in another app entirely, confirming the handoff is the only
|
||||
reassurance this app can offer.
|
||||
2. **Zero motion, and the thinnest local theme in the fleet** — it lacks
|
||||
`AppSpacing`/`AppRadius`/`AppTextSize`, so widgets copied from sibling apps
|
||||
will not compile here.
|
||||
|
||||
## what
|
||||
|
||||
`~/src/wake-alarm/phone_app` is the smallest surface in this project: 12 Dart files,
|
||||
a sync companion that sets a time, syncs it, and hands off to the system clock
|
||||
app. It has **zero motion, zero haptics, zero audio, zero progress feedback** —
|
||||
verified 2026-08-16. Its settings screen is a 56-line `StatelessWidget` with two
|
||||
`ListTile`s and no state at all.
|
||||
|
||||
Bring it up to the fleet's baseline: a confirmation the alarm was actually set,
|
||||
motion tokens on its transitions, and a haptic on the one action that matters.
|
||||
|
||||
**This app is deliberately last and deliberately small.** It also carries the
|
||||
project's sharpest hazard — see `must not` about alarm audio.
|
||||
|
||||
## where
|
||||
|
||||
Repo: `~/src/wake-alarm`. Flutter app: `~/src/wake-alarm/phone_app` (**underscore**, not
|
||||
`phone-app`). The repo root is a Python package; the Flutter app is only the
|
||||
`phone_app/` subdirectory.
|
||||
|
||||
Primary:
|
||||
- `phone_app/lib/screens/home_screen.dart` — `_setPhoneAlarm()` ~:86-96 builds an
|
||||
`AndroidIntent(action: 'android.intent.action.SET_ALARM', arguments: {HOUR,
|
||||
MINUTES, SKIP_UI: false, MESSAGE: 'Wake Alarm'})` via `android_intent_plus`.
|
||||
The `CircularProgressIndicator` is at ~:189. The only `Duration` in the whole
|
||||
app is `.timeout(const Duration(seconds: 3))` ~:95 — **an intent timeout, not
|
||||
animation. Do not tokenise it.**
|
||||
- `phone_app/lib/screens/settings_screen.dart` — 56 lines, `StatelessWidget`,
|
||||
`ListView` ~:21-53, `MaterialPageRoute` ~:30, :49.
|
||||
- `phone_app/lib/services/sync_settings.dart` — holds **only** the GitHub token
|
||||
in the keystore (`FlutterSecureStorage`, `_secureTokenKey = 'github_token'`
|
||||
~:31, `load()` ~:36, `save()` ~:61). There is no general app-preferences class.
|
||||
- `phone_app/lib/ui/theme.dart` — local hand-copy, **no `design_system`
|
||||
dependency**. `buildAppTheme()` ~:12, `AppStatusColors` ~:54. ⚠️ Unlike the
|
||||
other apps it does **not** declare `AppSpacing`/`AppRadius`/`AppTextSize`, so a
|
||||
widget copied from diet-guard or workout_app **will not compile here**. Wired at
|
||||
`phone_app/lib/main.dart` ~:18.
|
||||
|
||||
## must
|
||||
|
||||
**Step 1 — motion + haptics (no new dependency).**
|
||||
|
||||
- Add `HapticFeedback.mediumImpact()` when the alarm is successfully set — the
|
||||
single most important action in the app. Fire it on the tap, not after the
|
||||
intent round-trips. `HapticFeedback` is in `package:flutter/services.dart`; add
|
||||
`<uses-permission android:name="android.permission.VIBRATE"/>` to
|
||||
`phone_app/android/app/src/main/AndroidManifest.xml` if absent.
|
||||
- Give setting the alarm a **clear confirmation**: which time was set, and that
|
||||
the handoff to the system clock succeeded. Today the user gets a `_status`
|
||||
string and a spinner. Since the actual alarm lives in another app, confirming
|
||||
the handoff is the only reassurance this app can offer — make it unambiguous.
|
||||
- Transcribe the motion tokens from prompt 01 into `phone_app/lib/ui/theme.dart`,
|
||||
matching the existing hand-copy convention. Add `AppSpacing`/`AppRadius`/
|
||||
`AppTextSize` **only if** you need them; otherwise leave the file's shape alone.
|
||||
- Apply motion tokens to the two `MaterialPageRoute` transitions.
|
||||
- Honour `MediaQuery.of(context).disableAnimations`.
|
||||
|
||||
**Step 2 — sound (optional here; consider skipping).**
|
||||
|
||||
- A UI confirmation cue would need an audio dependency, an `assets:` block, a
|
||||
settings toggle, **and** a preferences class that does not exist yet (see
|
||||
below). For an app whose entire job is to hand off to another app, that is a
|
||||
poor value ratio.
|
||||
- **Recommendation: skip sound in this app** and say so explicitly in the session
|
||||
summary. If you do add it, it needs the full opt-out toggle like everywhere
|
||||
else — and the toggle work below.
|
||||
- To add any toggle here you must do three things the other apps do not require:
|
||||
(a) convert `SettingsScreen` from `StatelessWidget` to `StatefulWidget`,
|
||||
(b) create a preferences class — there is no `AppSettings` equivalent; model it
|
||||
on `~/src/todo/lib/data/app_settings.dart`, and note `shared_preferences: ^2.3.0`
|
||||
is **already a dependency**, (c) add the `SwitchListTile` to the `ListView`.
|
||||
|
||||
**Both steps:**
|
||||
|
||||
- must not: **touch anything under `~/src/wake-alarm/wake_alarm/`.** That is the
|
||||
desktop Python daemon that produces the real alarm noise — sine-tone WAVs via
|
||||
`paplay` → `aplay` → `speaker-test` (`_audio.py`, `_play_on_all_sinks` ~:218,
|
||||
pcspkr evdev fallback `_beep_pcspkr` ~:81), escalating 440Hz → 1000Hz → loud in
|
||||
the loop at `_alarm.py` ~:393-407, with sink volume save/restore in `_sinks.py`.
|
||||
**Never route a UI-sound opt-out through any of it.** A toggle that silences a
|
||||
wake-up alarm is a bug that makes someone late for work.
|
||||
- The two systems never share a process, package, or file — `phone_app` has no
|
||||
audio engine at all — so a UI sound layer here **structurally cannot** touch
|
||||
alarm playback. Keep it that way; do not build a bridge between them.
|
||||
- must not: change the alarm-setting intent, its arguments, or the 3s timeout.
|
||||
Feedback wraps that call; it does not modify it.
|
||||
- must not: add a streak, a counter, or any reward for setting an alarm. Waking
|
||||
up is not a metric this app owns, and rewarding "alarms set" rewards the wrong
|
||||
act entirely.
|
||||
- must not: copy a widget from another app without checking it only uses symbols
|
||||
this app's thinner theme actually declares.
|
||||
|
||||
## done
|
||||
|
||||
1. Setting an alarm produces a haptic and an unambiguous confirmation naming the
|
||||
time that was set.
|
||||
2. Page transitions use the shared motion tokens; no inline animation `Duration`
|
||||
remains (the 3s intent timeout stays).
|
||||
3. `cd ~/src/wake-alarm/phone_app && flutter analyze` is clean.
|
||||
4. `cd ~/src/wake-alarm/phone_app && flutter test` passes.
|
||||
5. With OS "remove animations" enabled, the app works and durations are zero.
|
||||
6. `git diff` shows **no changes under `~/src/wake-alarm/wake_alarm/`**.
|
||||
7. The summary states whether sound was added or deliberately skipped.
|
||||
|
||||
## verify
|
||||
|
||||
**On the phone.**
|
||||
|
||||
```
|
||||
adb devices # confirm 23181JEGR08034
|
||||
cd ~/src/wake-alarm/phone_app
|
||||
flutter build apk --release
|
||||
adb install -r build/app/outputs/flutter-apk/app-release.apk
|
||||
```
|
||||
|
||||
Never `flutter install`, never `adb uninstall`, never `pm clear` — the app stores
|
||||
a GitHub token in the keystore and wiping it means re-authenticating.
|
||||
|
||||
Then set a real alarm on the device: confirm the haptic fired (state it as a
|
||||
physical observation), the confirmation named the right time, and **the system
|
||||
Clock app actually received the alarm**. The last one is the real test — this
|
||||
app's job is the handoff, so verify the alarm exists in the Clock app, not just
|
||||
that this screen said so.
|
||||
|
||||
Do **not** test by letting an alarm actually fire unless kuhy asks — that
|
||||
involves the desktop daemon and real noise.
|
||||
|
||||
## read first
|
||||
|
||||
- `phone_app/lib/screens/home_screen.dart` — `_setPhoneAlarm()` in full,
|
||||
including the timeout and the `_status` string handling.
|
||||
- `phone_app/lib/ui/theme.dart` — note what it does **not** declare before
|
||||
writing any widget.
|
||||
- `~/src/todo/lib/data/app_settings.dart` — the preferences-class pattern, only if
|
||||
you decide to do step 2.
|
||||
- `~/src/utils/unified-design-system/motion.md` — vocabulary from prompt 01.
|
||||
**Prompt 01 must have run first.**
|
||||
|
||||
## context you would otherwise rediscover
|
||||
|
||||
- **The Flutter app produces no audio whatsoever.** No audio package, no
|
||||
`assets:` block; `assets/` contains only launcher icons. It fires an Android
|
||||
`SET_ALARM` intent and the **system Clock app** rings. The desktop Python
|
||||
daemon is an entirely separate program on a different machine.
|
||||
- The repo root (`~/src/wake-alarm/wake_alarm/`, `wake_alarm.egg-info`) is the Python
|
||||
package. Only `phone_app/` is Flutter.
|
||||
- This app has **no `design_system` dependency** and the thinnest local theme in
|
||||
the fleet. Adding the dependency is out of scope here (see prompt 07 for what
|
||||
that migration looks like when done properly).
|
||||
- No streaks, no charts, no progress, no completion counts exist — and none
|
||||
should be added. This is a two-button utility.
|
||||
|
||||
REMOVE ME AFTER FINISH
|
||||
Loading…
Reference in New Issue
Block a user