Close dopamine-ux 06-todo, log partial
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Krzysztof Rudnicki 2026-11-02 09:38:52 +01:00
parent 98c013ea3f
commit c25ae4e0d1
2 changed files with 1 additions and 183 deletions

View File

@ -103,7 +103,7 @@ what remains instead.
| 03-diet-guard.md | not started | — | — |
| 04-screen-locker.md | DONE 2026-11-01 | `89c65bd` | Status window `_section_streak` + week-transition reward text (kept in `extra_benefits_state.json`); the dead 09:00 early-bird claim is not shown. Per kuhy: no daily-streak state, no shutdown time, no freezes. Commit is on a worktree branch, unpushed. |
| 05-workout-app.md | DONE 2026-11-01 | `e938201` | Set/warmup `selectionClick`, finish `mediumImpact` + generated chime (music_theory, pinned tag) + dialog arrival, `AppDuration`/`AppCurve` in theme.dart, streak counts up, reduced motion honoured, one Settings "Sound" switch for chime and break-end (foreground service too). Worktree commits, unpushed; `62c1c2c` covers old export/import gaps. |
| 06-todo.md | not started | — | — |
| 06-todo.md | PARTIAL 2026-11-02 (step 1 DONE; step 2 sound REJECTED by kuhy) | `kuhyx/todo-app 2ffae26` | haptic + checkmark on capture, appRoute motion tokens, animated wizard bar; no sound by decision |
| 07-dufs-theme.md | not started | — | — |
| 08-dufs-motion.md | not started | — | — |
| 09-wake-alarm.md | not started | — | — |

View File

@ -1,182 +0,0 @@
Adopt the motion tokens in todo, and make capture feel immediate
## findings (3, ranked by value over effort)
1. **Capture gives no confirmation** — a successful capture is indistinguishable
from a no-op unless you go looking for the note.
2. **Zero motion, zero haptics** — despite being the only app already wired to
the shared package, so adoption here is cheapest.
3. **The pinned `design_system` tag predates the motion tokens** — nothing
compiles until the ref is bumped. Easy to forget, fatal if skipped.
## what
`todo` is the only app in kuhy's fleet already wired to the shared
`design_system` package — it has **deliberately no local `theme.dart`** (the
pubspec comment says so: nothing to drift from the frozen table). That makes it
the cheapest place to adopt the motion tokens added by prompt 01, and the
reference implementation the other apps get compared against.
Today it has **zero motion and zero haptics**. Verified 2026-08-16: no
`Animated*`, no `AnimationController`, no `HapticFeedback`, no audio. Its only
progress UI is the note-editor wizard bar. Capturing a note — the app's whole
purpose, and the thing that has to feel frictionless enough to actually do —
gives nothing back beyond a toast on error.
Per kuhy's rule for this project: **reward the capture, never the completion
count.** A todo app that celebrates finishing things teaches you to log easy
things; one that celebrates *capturing* keeps the backlog honest.
## where
Repo: `~/src/todo`.
**First step — bump the dependency.** `pubspec.yaml` pins `design_system` at git
ref `design_system-v0.1.0`, which predates the motion tokens. Bump it to
**`design_system-v0.2.0`** (cut by prompt 01) or the tag that prompt actually
printed. Without this, `AppDuration` does not exist and nothing else here compiles.
Primary:
- `lib/ui/capture_screen.dart`, `lib/ui/capture_screen_widget.dart`,
`lib/ui/capture_screen_sync.dart` — the capture path. `showError` ~:76 and
`showToast` ~:78 in `capture_screen_sync.dart` are the fleet's only uses of the
shared feedback helpers.
- `lib/ui/capture_app_bar.dart` — progress indicators ~:66, :84.
- `lib/ui/note_editor.dart` — `showError` ~:115.
- `lib/ui/note_editor_step_page.dart` ~:80 — `LinearProgressIndicator(value:
(index+1)/total)`, the wizard step bar. The one honest progress surface.
- `lib/ui/notes_list_screen.dart` — `_searchDebounce = 250ms` ~:45 (**not**
animation; do not tokenise it).
- `lib/ui/notes_list_navigation.dart`, `lib/ui/settings_screen.dart` (2×),
`lib/ui/capture_screen.dart` — the `MaterialPageRoute` sites, all using the
default platform transition.
Settings — **this app has the only working toggle in the fleet; it is the
pattern the other prompts copy**:
- `lib/ui/settings_screen.dart` ~:213-225 — `ValueListenableBuilder<AppSettings>`
wrapping a `SwitchListTile`; setter ~:111-140 (logs an `AnalyticsEvent`, opens
a Firebase client, calls `withAdvancedMode`).
- `lib/data/app_settings.dart` — `AppSettings` immutable class,
`SharedPreferences` keys ~:38-39, `load()` ~:42, `withAdvancedMode` ~:60,
`reconcileWithRemote` ~:98, `adopt` ~:132 (last-writer-wins cross-device sync
via `settings/advancedMode` ~:20). Held app-wide as a `ValueNotifier`.
Theme: no local file. `lib/main.dart` ~:4 imports `design_system`; ~:60-61 wires
`theme: buildLightTheme(), darkTheme: buildDarkTheme()`. Source of truth is
`~/src/utils/design_system/lib/src/theme.dart`.
## must
**Step 1 — motion + haptics (no new dependency).**
- Bump `design_system` to the new tag **first**, and confirm `AppDuration` /
`AppCurve` resolve before writing any other code.
- Add `HapticFeedback.selectionClick()` (or the token vocabulary's "confirm" cue)
on **successful note capture**. `HapticFeedback` comes from
`package:flutter/services.dart` — no pubspec change. Add
`<uses-permission android:name="android.permission.VIBRATE"/>` to
`android/app/src/main/AndroidManifest.xml`; it is absent today.
- Fire the haptic **on the tap**, not after the Firebase sync returns. The whole
point of the micro-animation idea is that immediate feedback removes the
perceived latency; a cue gated on a network round-trip reintroduces it.
- Give the capture a visible confirmation — currently a successful capture is
indistinguishable from a no-op unless you go looking.
- Animate the wizard `LinearProgressIndicator` between steps rather than jumping.
- Apply the shared page-transition/motion tokens to the `MaterialPageRoute` sites
so navigation matches the rest of the system.
- Honour `MediaQuery.of(context).disableAnimations`: durations collapse to zero,
haptics still fire.
**Step 2 — sound (separate, independently revertable).**
- Add an audio dependency and an `assets:` block to `pubspec.yaml` (neither
exists) plus a short capture-confirm cue.
- Sound ships **on**, with an opt-out `SwitchListTile` — copy the
`advancedMode` pattern verbatim (widget ~:213-225, setter ~:111-140, field in
`app_settings.dart`). It is the fleet's proven toggle; do not invent a new one.
- Consider whether the sound preference should sync across devices like
`advancedMode` does, or stay local. Local is the safer default — a per-device
audio choice is usually what people mean.
**Both steps:**
- must not: reward **completing** a todo more than capturing one. No streak on
completions, no counter that makes finishing feel better than recording. The
backlog is only useful if capture stays frictionless and honest.
- must not: add a variable/random reward to capture. There is no real pool to
draw from here, so any variance would be manufactured — which fails the
project's second test.
- must not: fabricate progress. The wizard bar's `(index+1)/total` is true; keep
it that way.
- must not: hand-copy token values into a local `theme.dart`. This app
deliberately has none — that is a feature, and the pubspec comment says so.
- must not: add `# noqa`-equivalents or suppress analyzer warnings.
- optional: animate the notes-list reordering/filtering, if it falls out cheaply
from the shared tokens.
## done
1. `pubspec.yaml` references the new `design_system` tag and `flutter pub get`
resolves it.
2. Capturing a note produces a haptic within ~50ms of the tap and a visible
confirmation.
3. Wizard step transitions and page navigation use shared motion tokens; no
inline `Duration` remains for animation purposes (the 250ms search debounce
and 5s auto-sync debounce stay as they are — they are not motion).
4. `cd ~/src/todo && flutter analyze` is clean.
5. `cd ~/src/todo && flutter test` passes.
6. With OS "remove animations" enabled, the app works and durations are zero.
7. Step 2: the sound toggle flips, persists across restart, and silences the cue.
## verify
**On the phone — todo is mobile-primary.**
```
adb devices # confirm 23181JEGR08034
cd ~/src/todo
flutter build apk --release
adb install -r build/app/outputs/flutter-apk/app-release.apk
```
Never `flutter install`, never `adb uninstall`, never `pm clear` — those wipe
real notes. `adb install -r` preserves them.
Then capture a real note on the device and report what the phone did. A haptic is
a physical outcome: say what you felt, do not infer it from code. Also step
through the note-editor wizard to confirm the progress bar animates.
If the app also has a desktop/web surface deployed under `/opt/`, note that it
can go stale independently — but this prompt's done-condition is the phone.
## read first
- `lib/ui/settings_screen.dart` ~:213-225 and `lib/data/app_settings.dart` — the
toggle pattern, in full, before writing the sound switch.
- `lib/ui/capture_screen_sync.dart` ~:76-78 — how `showToast`/`showError` are
used today; the shared helpers live in
`~/src/utils/design_system/lib/src/feedback.dart`.
- `~/src/utils/design_system/lib/src/feedback.dart` — `showToast`/`showError`, `_show`
~:50. `confirm.dart` beside it **already imports `flutter/services.dart`**, so
if prompt 01 added a shared haptic helper it lives near here.
- `~/src/utils/unified-design-system/motion.md` — the vocabulary from prompt 01.
**Prompt 01 must have run first, and its tag must be cut.**
- `pubspec.yaml` — read the comment explaining why there is no local theme.
## context you would otherwise rediscover
- `showToast` and `showError` already carry fixed dwell times in the shared
package (`_toastDuration = 3s`, `_errorDuration = 5s`,
`feedback.dart` ~:14, :18). Those are **not** motion tokens; leave them.
- This is the **only** app on `design_system`. diet-guard, workout_app and
wake_alarm hand-transcribe tokens into local theme files, so a widget copied
from this app into one of those will not compile unmodified.
- The app has no streaks, no rings, no history charts, and no completion counts
today. That is a deliberate starting point for this prompt, not an oversight to
fix wholesale.
- Settings sync cross-device last-writer-wins through Firebase
(`settings/advancedMode`). If you add a synced preference, follow
`reconcileWithRemote`/`adopt`; if local-only, skip that path entirely.
REMOVE ME AFTER FINISH