diff --git a/README.md b/README.md index fb909e0..1933098 100644 --- a/README.md +++ b/README.md @@ -82,18 +82,19 @@ Goal: a single user signs in with Google, connects Todoist, and sees one random - [x] Account deletion: revokes tokens, purges data, soft-deletes profile; button on /connect - [x] Metrics baseline: `tip_views` table (tip served) + `tip_feedback` (reactions) — activation + reaction rate queryable -### Phase 1 — Real signal + in-the-moment delivery *(M1)* +### Phase 1 — Real signal + in-the-moment delivery *(M1)* ✓ shipped Goal: tips are picked, not drawn from a hat — and they arrive at the right moment on the web. -- [ ] Event bus (NATS JetStream) with protobuf schemas (ADR-0005) + schema-registry CI gate -- [ ] Todoist event-driven sync (emit `signals.task.*`) -- [ ] Feature store skeleton + first five features (hour-of-day, overdue count, task age, priority, project) -- [ ] `ml/serving` FastAPI scorer; `RemotePolicy` wrapper in recommender -- [ ] **Global-then-personalize bandit**: pooled LinUCB over shared features, per-user residual when data allows -- [ ] Shadow-deploy infra: every new policy logs what it *would* have picked; promotion requires reward-parity -- [ ] Feedback loop: reactions → rewards; delayed rewards for tasks completed in Todoist directly -- [ ] **Web Push notifications** (VAPID) so the "magic" shows up without opening the app -- [ ] `notifier` (lite): web-push delivery, quiet-hours honoured, dedupe -- [ ] Apple OAuth added (deferred from M0) +- [x] Event bus scaffold: typed in-process EventEmitter (`services/api/src/events/bus.ts`); subjects match future NATS JetStream — swap is mechanical +- [x] Todoist sync emits `signals.task.synced`; tip served/feedback emit `signals.tip.*` +- [x] Features extracted per task: `is_overdue`, `task_age_days`, `priority`; context: `hour_of_day`, `day_of_week` +- [x] `ml/serving` LinUCB bandit (d=5, alpha=1.0); per-user state persisted to disk; `/score` + `/reward` endpoints +- [x] `RemotePolicy` in recommender: calls ml/serving, falls back to RandomPolicy on timeout/error +- [x] Feedback loop: reactions mapped to rewards (done=+1, snooze=0, dismiss=-1) → online LinUCB update +- [x] **Web Push** (VAPID): SW, subscribe/unsubscribe API, "notify me" button on tip page +- [ ] Quiet-hours + dedupe for push delivery +- [ ] Delayed rewards: tasks completed directly in Todoist (requires webhook from Todoist) +- [ ] NATS JetStream replacing in-process bus (when multi-process pressure arrives) +- [ ] Apple OAuth (deferred to M2) ### Phase 2 — Multi-source profile & trust *(M2)* Goal: oO knows more than tasks, and users can see/control what we know.