- 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>
5.5 KiB
5.5 KiB
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
git pull --rebase origin main- 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.apiBasegardé 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
.envracine : secrets backend — jamais commité
Commandes
# 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_TERMSscanné 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
~/engineseulement ; stores en imports explicites (testables sous vitest nu) ; toute mutation d'état passe parcanTransition(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) :LdSealdans les layouts ; SVGs danspublic/ - 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)