# libreDecision Boîte à outils de la première démocratie — décider ensemble, du trio à la population. v2 local-first : 6 routes de décision, 5 modalités, documents sous vote permanent, mandats, observatoire. Marque blanche par construction (seeds Ğ1 + Atelier du Canal). **Référence produit : `docs/dev/BLUEPRINT-V2.md`** — toute décision de comportement s'y adosse. ## Protocole de début de session 1. `git pull --rebase origin main` 2. Si l'objectif de la session n'est pas précisé, le demander ## Stack - **Frontend** : Nuxt 4 (Vue 3, TypeScript, SPA — SSR off) + Nuxt UI v3 + Pinia + UnoCSS ; package manager : npm ; port **3002** strict - **Local-first** : IndexedDB (idb-keyval) via `app/data/persistence.ts` — une clé `ld2:` par collectif, export/import de bundles JSON, seeds par le même chemin - **Backend v1** (FastAPI + SQLAlchemy async, port 8002) : conservé pour la **future synchro** — non requis pour le dev frontend ; `runtimeConfig.public.apiBase` gardé dans nuxt.config en prévision - Déploiement : Docker multi-stage + Traefik ; CI Woodpecker ## Structure ``` frontend/ app/ engine/ # moteurs PURS (zéro dépendance Nuxt) : threshold (+electionResult), # nuanced, modeParams, parametric (+crystallize), settings, state (canTransition), # triage, impact — import UNIQUEMENT via le barrel ~/engine data/ persistence.ts # IndexedDB local-first templates.ts # 7 gabarits de collectif (invariant : protocole Consentement partout) seeds/ # duniter-g1.bundle.json (324 Ko) + atelier-du-canal.bundle.json stores/ collective.ts # le tenant : collectif actif, état complet, cycle de vie des bundles decisions.ts # façade d'actions sans état propre (LWW updatedAt, persist debounced) composables/ # useMood (useLibreMood), useFeed (le Fil = sélecteur pur), useSearch components/ # par domaine : chemin/ decisions/ votes/ texts/ mandates/ feed/ # onboarding/ common/ (primitives Ld*) pages/ # 14 routes : / · decider · decisions (+[id], vote, observatoire) · # textes (+[slug], formules) · mandats (+[id], nouveau) · # creer (layout bare) · donnees lexicon.ts # SOURCE UNIQUE des libellés FR + FORBIDDEN_UI_TERMS types/domain.ts # le noyau du modèle tests/ # 342 tests vitest : engine/, stores/, seeds/ (non-perte), lexicon.spec.ts backend/ # v1 FastAPI conservé pour la synchro future — ne pas développer dessus scripts/export_seed_bundle.py # extrait seed.py → duniter-g1.bundle.json (source vivante = bundle) docker/ # compose + Dockerfiles docs/dev/BLUEPRINT-V2.md # la référence produit v2 public/hexagram-tsing*.svg # sceau 井 (#48 Tsing) ``` ## Données runtime - **postgres-data** / **ipfs-data** : volumes Docker du backend v1 — jamais écrasés - `.env` racine : secrets backend — jamais commité ## Commandes ```bash # Frontend (tout le dev v2) cd frontend && npm run dev # :3002 npm test # vitest — 342 tests, référence à maintenir npm run build # zéro erreur exigé avant commit # Seeds : le bundle est la source vivante ; seed.py (backend) est remplacé par cd backend && .venv/bin/python scripts/export_seed_bundle.py # régénère duniter-g1.bundle.json # → test de non-perte bloquant : tests/seeds/duniter-g1.spec.ts # Docker docker compose -f docker/docker-compose.yml up ``` ## Conventions / pièges - **UI français, code anglais** (variables, commentaires, docstrings) - **lexicon.ts d'abord** : aucun libellé UI en dur si le lexicon peut le porter ; ton = tutoyer la personne, sujet grammatical = la personne ou le collectif, jamais la formule ni le système - **Anti-lexique** (`tests/lexicon.spec.ts`) : `FORBIDDEN_UI_TERMS` scanné sur lexicon + templates `.vue` (insensible casse/accents) — « triage », « verdict », « déléguer »… restent des identifiants de code, jamais des textes affichés - **Invariants doctrinaux** : jamais de score sur une personne (attributs jamais affichés à autrui, re-votes visibles du seul auteur) ; jamais de contenu adopté sans geste humain (cristallisation, clôture de dossier, départage = gestes datés) ; repli universel → consent ; dérogation asymétrique (alourdir libre, alléger motivé) - **Budget 400 lignes max par page** (`app/pages/`) - **Moteurs purs** : engine/ sans Nuxt/Pinia/DOM, import via `~/engine` seulement ; stores en imports explicites (testables sous vitest nu) ; toute mutation d'état passe par `canTransition(decision, to, ctx)` - **Formule inertie** : `R = C + B^W + (M + (1−M)(1−(T/W)^G))·max(0, T−C)` — `frontend/app/engine/threshold.ts` ; l'Atelier (`/textes/formules`) est la seule maison des lettres W/T/M/B/G/C et de KaTeX - **Mood system** : `useLibreMood()` (`composables/useMood.ts`) — **jamais** de `:global()` dans `