- ADR-0003: modular monolith for Phase 0 with documented extraction triggers - ADR-0004: Auth.js + OIDC-shaped boundary; dedicated provider when mobile ships - ADR-0005: protobuf for events, OpenAPI for HTTP, schema-registry CI gate - New architecture docs: data-model, metrics (magic proxies), privacy (Phase-0 feature) - Prime directives updated: privacy-as-feature, modular-by-package-deployable-by-stage - Roadmap revised: Apple OAuth deferred to M1; web push in M1; k3s intermediate; tip-kind-aware UI - PLAN updated: Phase-0 deletion endpoint, metrics baseline, compose profiles, import-boundary lint - License decision in README (ARR with OSS plan in Phase 5)
1.4 KiB
1.4 KiB
recommender
The core of oO. Takes a user + a context, returns one tip.
Contract
POST /recommend
{ user_id, context?: { time, timezone, client, ... } }
→ { tip: { id, kind: "todo"|"advice", title, body, source, deep_link, meta } }
POST /feedback
{ user_id, tip_id, reaction: "done"|"snooze"|"dismiss", at }
Internals (stable seams)
- Candidate sources — pluggable async generators. v0: Todoist tasks via
integrations. Later: advice library, calendar nudges, health prompts. - Feature assembler — fills the
contextblob (inline in Phase 0; calls feature store from M1). Never inlined into policy code. - Policy registry —
Policy.pick(candidates, context) → tip. Named entries:random— v0 (Phase 0).bandit.linucb.pooled— v1 (Phase 1). Global-then-personalize: pooled features shared across users; per-user residual once data allows.remote— delegates toml/servingFastAPI scorer (Phase 1+).
- Shadow hook — every request optionally runs N shadow policies in parallel and logs their picks + estimated rewards. Promotion from shadow → A/B → launch is a separate, deliberate step (ADR-0002).
- TipInstance persistence — every decision writes
context_snapshot(features seen at decision time). This is what makes offline replay honest.
Phase 0 goal
RandomPolicy only. The service, contract, registry, shadow hook, and tip-instance persistence all exist; no ML yet.