v3 : déploiement (image unique, produit à la racine, maquette en sous-domaine) et documentation du chantier

- docker/app.Dockerfile : une image Rust + Svelte, la construction rejoue toute
  la boucle (fmt, clippy -D warnings, tests, svelte-check, vitest) ; utilisateur
  non-root, volume /data. docker/app-compose.yml pour sonic (fabio).
- .woodpecker/app.yml : produit sur decision.librodrome.org, attend la maquette,
  écarte tout conteneur qui revendique encore la racine, healthcheck du commit.
- .woodpecker/maquette.yml (ex .woodpecker.yml) : la maquette v2 telle quelle,
  sur maquette.decision.librodrome.org, mêmes volumes.
- .env.example de la maquette rangé dans maquette/ ; celui de la v3 à la racine.
- Docs : README, CONTRIBUTING (travail à deux), CLAUDE.md v3, HANDOFF,
  docs/chantiers (feuille de route, fiche, contrôles, onboarding de Chiara,
  demande à poka), docs/adr 0001–0006.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-21 03:50:44 +02:00
co-authored by Claude Opus 5.5
parent c2669a3d0b
commit 1f9c241b41
24 changed files with 1187 additions and 281 deletions
+19
View File
@@ -0,0 +1,19 @@
# 0001 — Rust + Svelte, une image, SQLite
**Contexte.** La v2 (Nuxt local-first + FastAPI) a servi de maquette. Yvv veut « un dev rust -
svelte pour la longue durée, la performance, la modularité et la compilation de haute qualité ».
Le premier chantier a besoin d'un serveur (votes partagés, publication), pas d'un stockage
navigateur.
**Décision.**
- Serveur en Rust : `ld-engine` (règles pures, sans I/O), `trustin-access` (accès),
`ld-server` (axum + sqlx). Toolchain épinglée, `clippy -D warnings`, `unsafe` interdit.
- Interface SvelteKit / Svelte 5 en SPA statique, compilée et servie par le même binaire.
- SQLite (WAL) dans un volume nommé : une instance, des milliers de votants, aucune base à
administrer. Migrations additives seulement.
- Une seule image ; sa construction rejoue toute la boucle.
- La maquette v2 reste dans `maquette/`, intacte, déployée à part.
**Coût.** Deux langages dans le dépôt ; une instance unique (pas de réplication). Si la
synchronisation entre instances devient nécessaire, elle passera par le dépôt des textes et une
API, pas par la base.
+20
View File
@@ -0,0 +1,20 @@
# 0002 — Vote permanent : marge du texte en place et plancher C
**Contexte.** Chaque article est sous vote permanent ; « dès qu'une proposition atteint le score,
elle remonte en vigueur ». La formule inertielle Ğ1 donne un seuil faible quand peu de gens
votent : sans garde-fou, une seule voix ferait passer un texte. Sans règle de départage, deux
versions qui atteignent leur seuil s'échangeraient la place à chaque vote.
**Décision.**
- Plancher **C = nombre minimal de votants « pour »** (Monnaie libre : 50 ; essai : 1), dans
la formule (`R = C + …`) — choix d'Yvv.
- Une proposition entre en vigueur si elle atteint son seuil **et** tient une marge
(pour − requis) strictement plus large que la version en place. La version déplacée redevient
une proposition avec ses votes : rien n'est perdu, tout peut revenir.
- Les textes de départ sont des *seeds a minima* (0 vote, marge −C) : visibles, non adoptés.
- Changer l'inertie d'un article est une proposition votée à l'inertie du cran au-dessus.
- Le balayage tourne après chaque geste et toutes les 5 minutes, sous un verrou global.
**Coût.** Une version en place très soutenue est difficile à déloger — c'est voulu. La taille
de la toile (W) bouge : un texte peut passer ou repasser sans nouveau vote ; chaque entrée en
vigueur est datée et chiffrée dans `adoptions`.
+15
View File
@@ -0,0 +1,15 @@
# 0003 — Corriger n'est pas voter
**Contexte.** « Ça ne peut pas devenir un système qui nécessite un vote pour corriger une
coquille. » Mais une correction ne doit pas pouvoir changer le sens d'un texte voté.
**Décision.** Le moteur classe chaque modification (`ld-engine/src/edit.rs`) :
- **coquille** si le squelette (sans accents, casse, ponctuation, espaces) est identique, ou si
au plus 2 mots diffèrent de 2 lettres au plus, sans chiffre ni mot de négation (ne, pas,
jamais, personne…) ; elle s'applique sans vote, garde les votes et les traductions ;
- **de fond** sinon : nouvelle révision d'une proposition ouverte (par son auteur), votes
antérieurs « à reconfirmer », signalements levés ; refusée sur un texte en vigueur.
L'éditeur montre le verdict avant d'enregistrer. Les brouillons s'éditent librement.
**Coût.** Le classement est prudent : certaines vraies coquilles (« persone » → « personne »)
passent pour du fond et demandent une proposition. On préfère ce sens d'erreur.
+14
View File
@@ -0,0 +1,14 @@
# 0004 — Contradictions : signalées par les personnes, bloquantes
**Contexte.** « Il faut que 2 unités modulaires ne puissent pas passer en vigueur si elles se
contredisent. » La contradiction est une question de sens : aucune règle ne la détecte sans
se tromper, et l'IA est écartée pour l'instant.
**Décision.** Tout membre signale une contradiction entre deux propositions, avec un motif.
Tant que le signalement tient, celle qui n'est pas en vigueur ne peut pas y entrer. Il se lève
par son auteur ou un administrateur, et tombe si la proposition signalée change sur le fond.
L'assistant, lui, prévient (sans bloquer) quand un texte porte plusieurs décisions ou ressemble
à une proposition existante.
**Coût.** Un signalement abusif bloque une proposition jusqu'à sa levée. Les signalements sont
publics, motivés, et l'administration peut les lever.
+17
View File
@@ -0,0 +1,17 @@
# 0005 — Publication par file d'envoi vers un dépôt git
**Contexte.** « Le tout est automatiquement poussé sur le dépôt git exploité par les applis
clientes. » Le réseau ou la forge peuvent être indisponibles ; un vote ne doit jamais échouer
pour autant.
**Décision.** Chaque entrée en vigueur (ou correction de coquille, ou traduction d'un texte en
vigueur) ajoute une ligne à une file d'envoi en base ; au démarrage, chaque document publié y
est remis une fois (rien ne se perd sur un redémarrage, un document inchangé ne fait pas de
commit : sa date est celle du dernier changement, jamais « maintenant »). Un travailleur rend `fr.md`, `en.md`,
`es.md` et `document.json` dans le clone du serveur, commit, pousse avec une clé de déploiement
générée par le serveur (sa partie publique s'affiche dans trustinWallet), et réessaie avec un
délai croissant. Dépôt : `git.duniter.org/Yvv/libredecision-textes`, un dossier par groupe et
par document. Le français fait foi ; les traductions sont marquées éditoriales.
**Coût.** La publication est asynchrone (quelques secondes, plus en cas de panne) ; l'état
de la file se lit dans trustinWallet.
@@ -0,0 +1,20 @@
# 0006 — Accès : connect.monnaie-libre.fr, invitations, trustinWallet
**Contexte.** « Le même accès que pour le forum Monnaie libre », « toute personne avec une clé
Duniter peut se loger et tout faire — sauf la gestion des accès », qui revient à trustinWallet
(nouveau nom de trustWallet, déjà pris).
**Décision.**
- Connexion Ğ1 : libreDecision est client OAuth2 de connect.monnaie-libre.fr (comme le forum).
Le compte est la clé d'identité (`idty:N`) ; on garde le nom affiché et le statut de membre.
- En attendant l'inscription du client : liens d'invitation à usage unique (14 jours), le
secret dans le fragment de l'URL (jamais dans les journaux du serveur).
- Sessions : cookie HttpOnly, SameSite=Lax, Secure en https, 30 jours, jeton haché en base ;
en-tête anti-CSRF sur toute écriture.
- Les règles (qui voit, qui ouvre, qui administre) vivent dans `trustin-access`, futur cœur du
service trustinWallet ; le serveur ne décide jamais seul.
- Premier administrateur : un lien d'invitation écrit au premier démarrage dans le volume de
données (0600), jamais dans les journaux.
**Coût.** Tant que connect n'est pas branché, chaque testeuse a besoin d'un lien. Quand
trustinWallet deviendra un service, `trustin-access` deviendra son client.
+13
View File
@@ -0,0 +1,13 @@
# Décisions d'architecture
Une décision durable par fichier : le contexte, la décision, ce qu'elle coûte. On n'efface
pas une décision : on en écrit une nouvelle qui la remplace, et on le note dans l'ancienne.
| # | Décision | Date |
|---|---|---|
| [0001](0001-rust-svelte.md) | Rust + Svelte, une image, SQLite | 2026-10-21 |
| [0002](0002-vote-permanent.md) | Vote permanent : marge du texte en place et plancher C | 2026-10-21 |
| [0003](0003-corriger-n-est-pas-voter.md) | Corriger n'est pas voter | 2026-10-21 |
| [0004](0004-contradictions-signalees.md) | Contradictions : signalées par les personnes, bloquantes | 2026-10-21 |
| [0005](0005-publication-git.md) | Publication par file d'envoi vers un dépôt git | 2026-10-21 |
| [0006](0006-acces-connect-trustinwallet.md) | Accès : connect.monnaie-libre.fr, invitations, trustinWallet | 2026-10-21 |