forked from yvv/decision
- 43 fichiers v1 supprimés (composants documents/protocols/sanctuary/toolbox, stores auth/documents/groups/mandates/organizations/protocols/votes, composables api/notifications/formula/websocket, utils doublons du moteur) - nuxt.config épuré (polkadot retiré, KaTeX et apiBase gardés), meta v2 - README.md, CONTRIBUTING.md, CLAUDE.md réécrits pour la v2 - Build zéro erreur, 342/342 tests verts Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
101 lines
5.5 KiB
Markdown
101 lines
5.5 KiB
Markdown
# 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:<collectiveId>` 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 `<style scoped>` pour les styles mood-dépendants (causa le bug dark mode veil)
|
||
- **Sceau** `井` (#48 Tsing) : `LdSeal` dans les layouts ; SVGs dans `public/`
|
||
- **CSS `drop-shadow()`** safe pour l'emboss ; `<filter>` SVG inline cause des artefacts
|
||
- **Composants** : `pathPrefix: false` — noms de fichiers uniques, auto-import
|
||
- **Domaine** : decision.librodrome.org (Woodpecker CI ; ancien dossier : Glibredecision)
|