# libreDecision Rendre possible la décision collective — en fonction du contexte, processus et modalités, boîte à outils. **v3 : Rust + Svelte**, un chantier à la fois, chacun livré en production avant le suivant (`docs/chantiers/README.md`). **Référence du chantier en cours : `docs/chantiers/acte-certification/CHANTIER.md`** — toute décision de comportement s'y adosse ; les décisions durables dans `docs/adr/`. La maquette v2 (Nuxt + FastAPI) vit dans `maquette/`, **intacte** (Yvv la présente) — ne pas y développer. ## Protocole de début de session 1. `git pull --rebase origin main` (puis la branche du chantier : `chantier/acte-certification`) 2. Lire `HANDOFF.md` (où on en est, ce qui attend Yvv) 3. Si l'objectif de la session n'est pas précisé, le demander ## Stack - **Serveur** : Rust edition 2024, toolchain **1.99** épinglée (`rust-toolchain.toml`) ; rustup installé sans PATH → `export PATH=$HOME/.cargo/bin:$PATH`. axum 0.8, sqlx 0.8 (SQLite, requêtes runtime + `migrate!`), tokio, reqwest rustls. - **Interface** : SvelteKit 2 / Svelte 5 (runes), adapter-static SPA, vite 7, vitest, Playwright ; npm. Polices Literata (textes) + Nunito (interface), fontsource. - **Ports** : dev interface **3002** (strict, proxy `/api` → 8002) · serveur **8002** · serveur jetable des e2e **8092** (sa chaîne Ğ1 simulée prend un port local éphémère). - **Déploiement** : une image (`docker/app.Dockerfile`), Woodpecker `ci.open.us.org` dépôt LPK/decision → sonic (fabio). `.woodpecker/app.yml` = le produit sur **decision.librodrome.org** ; `.woodpecker/maquette.yml` = la maquette sur **maquette.decision.librodrome.org** (le produit passe en premier et retire de la racine tout conteneur qui la revendique ; la maquette attend un déploiement réussi du produit). Push sur `prod` (`gitea@git.open.us.org:EHV/decision.git`) branche `main` = déploiement. ## Structure ``` crates/ ld-engine/ # PUR (zéro I/O) : threshold (formule inertielle sur un quorum, judged_voters, # yes_needed / no_needed), inertia (cinq crans, leurs quorums, bornes, médiane, # référence, échelle), edit (coquille vs fond, diff), assist (découpage, similarité, # longueur), compose (adoption, aperçus), ballot (bulletin scellé ld1:, le dernier # fait foi, jugement d'une transaction), address (SS58, préfixe g1), objection # (redit / contredit : la dernière arrivée retenue, les tours, l'échéance), correction # (la coquille d'un autre : attendre, valider, refuser, l'échéance), order (l'ordre # d'une section : qui en est, complété à la fin, places), pending (modules à voter) trustin-access/ # règles d'accès de trustinWallet : sees / opens / manages_access, vote_way (serveur ou # chaîne), answers_typo, lifts_objection / reiterates_objection, reads_message, # sets_quorum, groupes par défaut (TEST_GROUP) ld-server/ migrations/ # schéma SQLite — additif seulement src/ # lib.rs (routes, csrf, run) · config.rs (LD_*) · auth.rs (connect OAuth2, invitations, # dev, bootstrap) · team.rs (liens d'équipe) · docs.rs (lecture, aperçus) · proposals.rs (écrire, voter, # traduire) · objections.rs (faire, lever, réitérer, la version retenue) · typos.rs # (les coquilles des autres) · messages.rs (la boîte « Messages ») · deadlines.rs # (les échéances de 3 jours) · clock.rs (horloge injectable) · adoption.rs # (balayage, verrou) · participation.rs (p̃, une fois par # jour) · publish.rs (file → git) · wot.rs (W sur la chaîne) · seed.rs · # seed_amend.rs / inertia_amend.rs / seed_proposal_amend.rs (amendements gardés ; # le dernier fait des Propositions seed, ADR 0011) · ballots.rs (vote groupé, # bulletins de la chaîne Ğ1, confirmation, « ton vote ») · indexer.rs (txComments) # · modules.rs / order.rs / doc_title.rs (les modules, ADR 0009 : l'ordre d'une # section, le titre du document) · module_amend.rs (ajout gardé) · quorum.rs (le quorum # d'un article, PUT /units/{id}/quorum ; « un vote existe » : voted_units, ADR 0013) · # quorum_amend.rs (les quorums par cran, amendement gardé) · web.rs (sert web/build) tests/ # api.rs (scénarios bout en bout) · ballots.rs (vote par la chaîne, indexeur simulé) · # objections.rs · typos.rs (lot F, horloge avancée, migration 0008) · modules.rs (ordre, # titre, ajout gardé) · seed_proposals.rs (Propositions seed, publication suspendue) · # quorum.rs (quorums par cran, la route, « un vote existe », l'amendement) · # fixtures/seed-production-2026-11-02/ (le seed que la production fait tourner : # I1/E1/E2 en vigueur, pour rejouer les amendements sur une base comme elle) seeds/ # acte-engagement-certification.json (trilingue, 33 articles, aucun en vigueur ; # I1/E1/E2 Propositions seed au cran 2 (seed::SEED_PROPOSAL_LEVEL), inertie 3 ailleurs ; # modules en vigueur sans vote : le titre à 4, l'ordre de chaque section à 2 ; # quorums par cran : 12 · 12 · 50 · 50 · 50 Monnaie libre, 1 partout essai) # upcoming/.json : les « À venir » des outils d'un groupe (fr, en, es), lus au démarrage web/src/ lib/i18n/ # fr.ts SOURCE (types Key) · en.ts · es.ts — parité testée lib/components/ # Sidebar, DocPanel, VoteFold (le vote déplié sous l'article), BallotPanel (panier), # ChainSign (signature, Ğecko), VersionDetails, ProposeView, UnitBlock, DeepList, # OrderModule / OrderEditor (l'ordre d'une section, la liste où l'on clique), # TitleBar / TitlePropose (le titre du document), Seal… lib/wallet/ # injected.ts (portefeuille injectedWeb3, polkadot-api chargé à la signature) · gecko.ts lib/basket.ts # PUR : sections, tout pour / tout contre, versions en concurrence, versions retenues, modules lib/objections.ts # où l'auteur répond à une objection · lib/diff.ts : le diff resserré d'une coquille lib/order.ts # PUR : placer, monter / descendre, ce qui a bougé routes/ # / (accueil + choix du groupe) · connexion · invitation · equipe · g/[group] (piliers, en cours) · # g/[group]/docs · g/[group]/docs/[doc] (lecteur, # vote déplié ?vote=, panneau ?onglet=panier|propositions|proposer|details|reglages) # · g/[group]/decisions · g/[group]/mandats · # g/[group]/protocoles (à venir) · acces (trustinWallet) · messages (la boîte) ; # /groupes, /decisions… redirigent web/e2e/ # Playwright C1–C33 + M1 ; serve.sh = serveur jetable /tmp/ld-e2e sur :8092 et # chain-mock.mjs (nœud et indexeur Ğ1 simulés) ; faux portefeuille dans helpers.ts docker/ # app.Dockerfile · app-compose.yml (sonic) .woodpecker/ # app.yml · maquette.yml docs/ # chantiers/ · adr/ · dev/BLUEPRINT-V2.md (doctrine de la maquette) · recherche/ maquette/ # v2 intacte (frontend Nuxt, backend FastAPI, docker, docs-v1) ``` ## Données runtime — jamais écrasées - **Production** : volume `lpk-libredecision-app_data` (`/data` : `libredecision.db`, `textes/` = clone du dépôt publié, `keys/` = clé de publication, `bootstrap-admin.txt`). Jamais `down -v`, jamais recréé. - **Maquette** : volumes `lpk-decision-main_postgres-data` / `_ipfs-data` (inchangés). - **Local** : `data/` (gitignoré) — le supprimer repart du seed. Les e2e utilisent `/tmp/ld-e2e`. - Un document seedé n'est **jamais** réinstancié : dès qu'il existe en base, c'est de la donnée. ## Commandes — la boucle (tout vert = fait) ```bash export PATH=$HOME/.cargo/bin:$PATH cargo fmt --all --check && cargo clippy --workspace --all-targets -- -D warnings && cargo test --workspace cd web && npm run check && npm test && npm run build && npm run e2e # e2e : 34 contrôles (35 tests, C25 en deux) LD_DEV_LOGIN=1 cargo run -p ld-server # :8002, sert web/build cd web && npm run dev # :3002 à chaud docker build -f docker/app.Dockerfile --target production . # rejoue toute la boucle ``` ## Conventions / pièges - **UI français, code anglais** ; **trilingue** : aucune chaîne affichée en dur, tout passe par `web/src/lib/i18n/fr.ts` puis en/es (test de parité + `FORBIDDEN_UI_TERMS`). Tutoyer ; le sujet est la personne ou le groupe, jamais le système. Le français du texte voté fait foi. - **Moteur pur** : une règle de vote / édition / composition s'écrit et se teste dans `ld-engine` avant d'être câblée. Le serveur ne décide jamais l'accès seul : `trustin-access`. - **Invariants** : au départ, aucun article n'est en vigueur : I1, E1 et E2 sont des Propositions seed (ouvertes, mises en avant, au cran 2 ; seuls les modules sont des *seeds a minima*), et la publication d'un document attend son premier texte en vigueur (ADR 0011) ; un quorum par cran (12 · 12 · 50 · 50 · 50 dans Monnaie libre), que l'auteur d'un article change avant le premier vote : sous le quorum, les votants qui manquent comptent comme « non », et une seule voix ne fait jamais passer un texte (quorum ≥ 2), sauf dans le groupe d'essai (quorum 1 ; ADR 0013) ; une coquille ne demande pas de vote, un changement de fond si ; la coquille d'un autre attend son auteur (un administrateur pour le seed) ou 3 jours, et en vigueur seule une coquille validée change le texte ; une objection retient la dernière arrivée jusqu'à sa levée (jamais un texte en vigueur ; ADR 0010) : elle n'entre pas en vigueur, et on peut y retirer son vote ou passer à « contre », jamais ajouter un « pour » (Yvv, 01/11/2026 : « Retirer ou "contre" ») ; l'inertie de l'inertie hérite du cran de l'article (cinq crans, ADR 0007) ; le groupe d'essai ne publie jamais ; dans Monnaie libre, un vote est une transaction Ğ1 et le dernier fait foi dans l'ordre de la chaîne (ADR 0008) — **jamais de transaction réelle en test** (chaîne simulée ; un essai réel est un acte « paye » d'Yvv). - **Écritures** : en-tête `x-requested-with: libredecision` obligatoire (CSRF) ; cookie `ld_session`. Les tests d'API passent par `Client` (`tests/api.rs`). - **Pièges vus** : `pkill -f target/debug/ld-server` tue son propre shell → `pkill -x ld-server` ; un `from_secret` absent fait refuser tout le pipeline Woodpecker ; la route hôte `/*` de fabio masque le webroot ACME → arrêter le service avant `acme.sh --issue` ; Playwright en locale `fr-FR` (sinon l'interface part en anglais). - **Design** : sans bordures, arrondis 16/12/20/14, quatre ambiances `pierre`, `calcaire` (claires), `nuit`, `ardoise` (sombres) (`data-mood`, posé avant le premier rendu, clé `ld:mood`), logo « Clé de voûte » (`Logo`, jamais incliné, `--cle` par ambiance), sceau hexagramme 井 #48 en SVG (`Seal` — jamais le glyphe, qui s'affiche mal). Mouvement réduit respecté. - **Domaine** : decision.librodrome.org ; dépôt des textes `git.open.us.org/yvv/decision-depot-textes-reference` (provisoire : puis un dépôt pour l'ouverture, puis le dépôt officiel pour les textes en vigueur).