ordinarthur-os/PLAN.md
ordinarthur 9c93e74318 replace Supabase with Postgres + Drizzle ORM
- Drop @supabase/supabase-js entirely; add drizzle-orm + postgres (porsager) driver
- New packages/db: schema (pgSchema ordinarthur_os), client factory, migrate runner, drizzle-kit config
- SQL migrations: 0000_init (pgcrypto + schema), 0001_jobs (jobs + job_search_criteria, no RLS)
- Rewrite apps/api db module with DI symbols DB/DB_HANDLE + @InjectDb() decorator
- Rewrite jobs.service.ts with Drizzle queries (upsert via onConflictDoUpdate, arrayOverlaps for stack filter)
- Replace SUPABASE_* env vars with DATABASE_URL in env config + .env.example
- Add docker-compose.yml (Postgres 16-alpine, dev only)
- Add deploy/k8s/postgres.yaml (StatefulSet + PVC), migrate.job.yaml, updated secrets.template.yaml
- Update all docs (README, PLAN, ARCHITECTURE, CLAUDE.md, AGENTS.md, packages/db/README.md)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 10:15:34 +02:00

120 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ordinarthur-os — Plan d'implémentation
> **Status** : planning terminé 2026-04-15. À implémenter via Claude Code (Sonnet).
## Vision produit
Un assistant personnel qui aide Arthur à s'organiser **sans le déresponsabiliser**.
- Dashboard clair de ce qu'il fait / veut faire
- Aucune action automatique invasive : l'IA propose, Arthur confirme d'un clic
- Pas de "weekly review" automatique, pas de nudges
- Une fonctionnalité signature : le **bouton "Parler"** — enregistrement vocal → transcription → création d'une todo / idée / étape projet / événement agenda, avec validation explicite avant écriture en base
## Principes directeurs
1. **Self-hosted, open-source**. Pas de Vercel, pas de Next.js. Tout tourne sur le k3s d'Arthur.
2. **Single-user**. Pas de multi-tenant, pas d'invitations, pas de partage. Bearer token unique pour protéger l'API.
3. **PWA installable iOS**. Vite + React, pas de SSR. Service worker + mutation queue pour l'offline.
4. **BFF unique**. La PWA ne parle qu'au NestJS. Le Nest parle à Postgres, Mistral, Groq, Google Calendar, Telegram.
5. **Design éditorial / Swiss-brutalist** — mirror du portfolio arthurbarre.fr (cream, ink, orange, borders, mono labels).
## Stack verrouillée
| Couche | Choix |
| --- | --- |
| Monorepo | pnpm workspaces + Turborepo |
| Frontend | Vite + React 18 + TanStack Router + TanStack Query + Tailwind + shadcn/ui |
| Backend | NestJS + Drizzle ORM (driver `postgres`) |
| DB | Postgres 16 standalone (k3s StatefulSet + PVC), schéma dédié `ordinarthur_os` |
| Auth | Bearer token statique (single-user), middleware Nest |
| IA LLM | Mistral `mistral-small-latest` (low-cost) via API |
| STT | Groq `whisper-large-v3-turbo` |
| Bot | Telegram Bot API (webhook) |
| Calendar | Google Calendar API (Apple souscrit au calendar Google via webcal) |
| Langue IA | FR only |
| Déploiement | Images Docker → Gitea Container Registry → pipeline Gitea Actions → k3s (Traefik + cert-manager supposés présents) |
| Backups | CronJob k8s `pg_dump --schema=ordinarthur_os` quotidien → stockage S3-compatible (à confirmer avec Arthur) |
## Roadmap — ordre d'implémentation
### Phase 0 — Scaffold (prio immédiate)
- Monorepo `pnpm-workspace.yaml`, `turbo.json`
- `apps/pwa` : Vite + React + Tailwind + shadcn + manifest PWA + service worker placeholder + routing TanStack
- `apps/api` : NestJS + module `health` + middleware bearer + client Drizzle initialisé
- `packages/shared` : types et zod schemas partagés
- `packages/db` : schéma Drizzle + premières migrations SQL (`0000_init` crée le schéma `ordinarthur_os`)
- `deploy/k8s` : manifests génériques (à adapter ensuite à la conf Gitea/Traefik d'Arthur)
- Design system : composants primitifs (`<Label>`, `<SectionHeader>`, `<GridFrame>`, `<DataChip>`, `<MetaRow>`) qui reproduisent le style arthurbarre.fr
- Routes `GET /health` et `POST /auth/verify`
### Phase 1 — Jobs (prio haute, remontée)
- Migration : tables `jobs`, `job_search_criteria`
- API : `POST /jobs/ingest` (bearer), `GET /jobs`, `PATCH /jobs/:id`, `GET/PUT /jobs/criteria`
- PWA : route `/jobs` avec filtres (toutes / remote / hybrid / marseille), rendu éditorial façon portfolio (lignes tableau, pas des cards violettes)
- PWA : route `/settings/jobs` pour éditer les critères (titres, localisations, stack[], remote_types[], salary_min, active)
- Dedup : clé unique `source_url`, update `last_seen_at` si déjà vu
- Rétention : jobs >30j auto-archivés (soft delete via `archived=true`)
- Le scheduled task Claude Code (hors repo) lit `/jobs/criteria?active=true` et push les résultats via `/jobs/ingest` quotidiennement à 7h
### Phase 2 — Todos riches
- Migration : table `todos` (voir ARCHITECTURE.md pour le schéma complet)
- API : CRUD + endpoints `/todos/:id/ai-enrich` (renvoie draft, ne sauve pas) et `/ai-enrich/apply` (après confirmation)
- PWA : route `/todos` avec inbox, filtres (status, priority, context, tags, project), édition inline
- Offline : mutation queue via Dexie, replay à la reconnexion, déduplication côté API via table `client_mutations`
### Phase 3 — Projets + Kanban
- Migrations : `projects`, `project_steps`, `project_ideas`
- API : CRUD projets, CRUD steps, reorder, CRUD ideas
- PWA : route `/projects`, détail projet avec kanban (colonnes backlog/todo/doing/review/done), zone idées
### Phase 4 — Agenda + Google Calendar sync
- Migration : `calendar_events`
- API : OAuth Google (scope calendar), CRUD events avec sync bi-directionnelle, endpoint `/agenda/ical/:secret.ics` pour souscription Apple
- PWA : route `/agenda` vue semaine + jour
### Phase 5 — IA : bouton texte + voice magic button
- Migration : `ai_actions` (log d'audit)
- API : `POST /ai/command` (texte → function calling Mistral → plan), `POST /ai/voice` (audio → Groq Whisper → texte → Mistral → plan), `POST /ai/command/confirm` (applique après clic user)
- Fonctions exposées au LLM : `create_todo`, `add_project_idea`, `add_project_step`, `create_calendar_event`, `toggle_daily_checkin`
- PWA : bouton "🎤 Parler" sur le dashboard, `Cmd-K` pour la barre de commande texte, modal de confirmation avant exécution
### Phase 6 — Telegram bot
- Webhook Nest `/telegram/webhook` signé (header `X-Telegram-Bot-Api-Secret-Token`)
- Commandes : `/today` (events + todos du jour), `/todo <texte>` (crée une todo), messages vocaux traités comme le voice magic button
- Rappel quotidien optionnel à une heure configurable (simple message, pas d'action auto)
### Phase 7 — Health tab
- Migration : `daily_checkins (day date PK, meds_taken boolean, note text?)`
- API : `GET /health/today`, `POST /health/today/toggle`
- PWA : slider/toggle simple "médocs pris aujourd'hui" + historique 30j minimaliste
### Phase 8 — Finance (reporté)
- Revolut perso n'a pas d'API → passer par GoCardless Bank Account Data (ex-Nordigen)
- À traiter uniquement quand les phases 07 sont stables
## Handoff Claude Code
Pour reprendre ce projet avec Claude Code (Sonnet) :
1. Lire `README.md`, `PLAN.md`, `ARCHITECTURE.md` dans cet ordre
2. Pointer `CLAUDE.md` du repo vers ces docs
3. Respecter les règles de collaboration d'Arthur :
- Pas de Next.js, pas de Vercel
- L'IA ne mute jamais la DB sans clic de confirmation
- Design = portfolio arthurbarre.fr (pas le violet/cyan du HTML jobs)
4. Avant de scaffolder, récupérer de l'utilisateur :
- Le dossier `/Users/arthurbarre/dev/perso/proxmox` (conf k3s) pour aligner les manifests
- Le skill `/deploy` ou `/create-deployment` qu'Arthur utilise pour ses autres déploiements Gitea
- Les secrets nécessaires : `MISTRAL_API_KEY`, `GROQ_API_KEY`, `TELEGRAM_BOT_TOKEN`, `GOOGLE_OAUTH_CLIENT_ID/SECRET`, `DATABASE_URL` (+ `POSTGRES_USER`/`POSTGRES_PASSWORD`/`POSTGRES_DB` côté StatefulSet), `API_BEARER_TOKEN`
- Le choix du stockage backup S3-compatible (B2 / Scaleway / autre)
5. Attaquer par la Phase 0 (scaffold), puis Phase 1 (jobs) — c'est explicitement prioritaire dans la tête d'Arthur.
## Points ouverts à trancher avec Arthur
- Stockage backups (quel bucket S3-compatible ?)
- Gitea Container Registry vs GHCR (défaut proposé : Gitea CR, déjà présent dans sa stack)
- STT Groq → clé à créer côté Arthur
- Google OAuth → app Google Cloud à créer, redirect URI `https://api.os.arthurbarre.fr/agenda/google/oauth/callback`
- Bot Telegram → @BotFather → récupérer le token, configurer le webhook vers `https://api.os.arthurbarre.fr/telegram/webhook`