# oO > One tip. Right now. Feels like magic. oO learns who you are from the apps you already use and surfaces **one** perfectly-timed suggestion — an advice or a todo — on a black page. No feed. No dashboard. One tip. --- ## Why Everyone has too many tasks, too many apps, too much noise. What people actually need is a single, well-chosen nudge at the right moment. oO is that nudge, powered by a recommendation engine that gets smarter the more of your life it sees. ## Product principles 1. **One thing at a time.** The UI is a black page with one tip. That's the product. 2. **We don't own your data, we understand it.** Connect your apps; we read what we need, when we need it. 3. **Magic requires craft.** Precision, timing, and restraint matter more than features. 4. **Private by default.** Tokens are encrypted, models are per-user, deletion is one click. ## Prototype scope (Phase 0) Three pages. That's it. | Page | What it does | |------|--------------| | **Sign in** | Google / Apple OAuth. No passwords. | | **Connect** | A list of integrations. Tap "Todoist" → OAuth flow → token stored. | | **Tip** | Black page. One tip. Tap to dismiss / done / snooze. | Under the hood the "pick a tip" call already routes through a `recommender` service with a pluggable policy — so v0 is literally "random Todoist task" but every other version slots into the same contract. --- ## Architecture at a glance ``` ┌──────────┐ OAuth ┌────────────┐ │ Web / │──────────▶│ auth │ │ Mobile │ └─────┬──────┘ │ client │ │ JWT │ │ REST/GraphQL ▼ │ │────────▶┌───────────────┐ └──────────┘ │ gateway │──┬──▶ profile └───────┬───────┘ ├──▶ integrations ──▶ Todoist / Google / ... │ └──▶ recommender ──▶ ml/serving (Python) ▼ ┌───────────────┐ │ events │ ◀── integrations emit normalized events │ (Kafka/NATS) │ ──▶ ml/pipelines (features, training) └───────────────┘ ``` More detail in [`docs/architecture/`](docs/architecture/) and decisions in [`docs/adr/`](docs/adr/). ## Monorepo layout See [`CLAUDE.md`](CLAUDE.md) for the full tree and conventions. ``` apps/ web, ios, android services/ gateway, auth, profile, integrations, recommender, events, notifier packages/ shared-types, sdk-js, ui ml/ pipelines, features, registry, experiments, serving infra/ docker, k8s, terraform, ci docs/ architecture, adr, api ``` --- ## Roadmap ### Phase 0 — Walking skeleton *(M0)* ✓ shipped Goal: a single user signs in with Google, connects Todoist, and sees one random Todoist task on a black page. Deletion works. - [x] Monorepo scaffold, docker-compose dev env - [x] `auth` — Google OAuth2/PKCE via openid-client v6; session cookie; Next.js middleware guard - [x] `integrations/todoist` — OAuth2 flow, token stored in DB, disconnect supported - [x] `recommender` with `RandomPolicy`; stable `POST /recommend` contract; 30s task cache - [x] `apps/web` — sign-in, connect, tip pages; PWA manifest + icons - [x] Feedback endpoint (done/dismiss/snooze); marks task complete in Todoist - [x] Deploy modular monolith to Agap VM via Caddy at `o.alogins.net` - [x] ToS + Privacy Policy pages (`/legal/terms`, `/legal/privacy`); implicit consent on sign-in - [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)* ✓ shipped Goal: tips are picked, not drawn from a hat — and they arrive at the right moment on the web. - [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. - [ ] Integrations: Google Calendar, Apple Health (web import), generic webhook ingress - [ ] Unified `Profile` model (identity, preferences, contexts, consents) - [ ] Timing signals (Page Visibility, Idle Detection, coarse location) — opt-in, transparent - [ ] Advice library + mixing policy (todo vs advice vs ambient) - [ ] User-facing data dashboard: what's stored, what's computed, export, delete-by-category - [ ] Cost/usage observability ### Phase 3 — Native mobile *(M3)* - [ ] iOS app (SwiftUI) with APNs push - [ ] Android app (Compose) with FCM push - [ ] `notifier` gains APNs + FCM channels, per-device rate limits - [ ] Migrate auth from Auth.js to dedicated OIDC provider (trigger from ADR-0004) - [ ] Decide-and-deliver scheduler: per-user "is this tip worth interrupting now?" threshold ### Phase 4 — MLOps at scale *(M4)* - [ ] Prefect/Airflow for batch feature materialization + retraining - [ ] MLflow registry; shadow → A/B → launch pipeline as first-class - [ ] Online experiments framework: deterministic assignment + bandit policies alongside fixed-split A/B - [ ] Cross-user collaborative features (opt-in only); cohort slicing; fairness checks - [ ] Drift monitoring (feature drift, prediction drift, reward drift); model cards per version ### Phase 5 — Production hardening *(M5)* - [ ] Audit logging, rotation of provider tokens + internal signing keys - [ ] **k3s** on existing VM, then k8s + HPA once multi-node justified (no cliff) - [ ] Multi-region failover, Postgres PITR, event-bus mirroring - [ ] Public integration SDK; sandbox tenancy for third-party connectors - [ ] Billing + subscription tiers --- ## Contributing This repo is split into independent modules; most tickets belong to exactly one. Pick an issue, check its milestone (= phase), read the service's `README.md`, ship. Conventions and per-service guidance live in [`CLAUDE.md`](CLAUDE.md). ## License All rights reserved — 2026. Contact the owner for licensing inquiries. (We'll switch to an OSS license for non-sensitive packages once the public SDK lands in Phase 5.)