forked from yvv/decision
Compare commits
13
Commits
94ddb17564
...
bb4bdf58c1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb4bdf58c1 | ||
|
|
abc9c44a55 | ||
|
|
92ba485f71 | ||
|
|
be72a0e188 | ||
|
|
a152652a07 | ||
|
|
df24ce1d80 | ||
|
|
9bb6023e70 | ||
|
|
bc5a3737f1 | ||
|
|
32fcf229ef | ||
|
|
39d5cd2633 | ||
|
|
5c9307bb9f | ||
|
|
4115b1c8ac | ||
|
|
bf9478039b |
+21
-2
@@ -1,8 +1,27 @@
|
|||||||
# HANDOFF — libreDecision
|
# HANDOFF — libreDecision
|
||||||
|
|
||||||
> Dernière mise à jour : 2026-11-02 à 03 h 10, session orch du chantier 1 (revue d'Yvv du 30/10).
|
> Dernière mise à jour : 2026-11-03 à 07 h 06, intégrateur libredecision-d4 (orch : dev-63, session
|
||||||
|
> racine /DEV, mandat « déploie » et « pousse » du 03/11 au 08/11). État vivant : le tableau
|
||||||
|
> `docs/chantiers/acte-certification/tableau.md`.
|
||||||
|
|
||||||
## Où on en est
|
## Où on en est (03/11)
|
||||||
|
|
||||||
|
- **M6 en ligne** depuis le 03/11 à 07 h 03 (`94ddb17`, pipeline 37 ;
|
||||||
|
`actes/2026-11-03-m6-propositions-seed-quorum-tampon.md`) :
|
||||||
|
- l'accueil sous la voûte (lot H) ;
|
||||||
|
- les Propositions seed : I1, E1, E2 ouvertes, au cran 2, aucun article en vigueur (ADR 0011) ;
|
||||||
|
- le quorum par cran, 12 · 12 · 50 · 50 · 50 en Monnaie libre, les votants manquants comptent
|
||||||
|
« non » (ADR 0013) ; la licence `AGPL-3.0-or-later` (lot S) ;
|
||||||
|
- le tampon des votes et le **vote simulé ouvert dans Monnaie libre** (pas de `LD_VOTE_ACCOUNT`),
|
||||||
|
marqué « simulé », qui compte puis s'efface par l'acte « supprime » à l'ouverture de la chaîne
|
||||||
|
(`ld-server discard-simulation`, serveur arrêté) ; la publication **pas avant des votes prouvés**
|
||||||
|
(lot V, ADR 0012). Le dépôt des textes reste à `9e0f45a`.
|
||||||
|
- **À dire à Yvv** : pas de lien d'équipe vers Monnaie libre pendant la simulation (chaque ouverture
|
||||||
|
est une voix simulée de plus).
|
||||||
|
- **En cours** : lot T (F1.1, F1.2, N1.1 réécrits sur le quorum, fichier de relecture pour Yvv), puis
|
||||||
|
lot U (le lecteur, M7). Liste d'attente de la relecture de M6 : au tableau.
|
||||||
|
|
||||||
|
## Avant le 03/11
|
||||||
|
|
||||||
**Revue d'Yvv du 2026-10-30** : treize points (L1 à L13), suivis dans
|
**Revue d'Yvv du 2026-10-30** : treize points (L1 à L13), suivis dans
|
||||||
`docs/chantiers/acte-certification/tableau.md` (lots, questions, actes ; source d'état).
|
`docs/chantiers/acte-certification/tableau.md` (lots, questions, actes ; source d'état).
|
||||||
|
|||||||
@@ -0,0 +1,249 @@
|
|||||||
|
# Acte — Mise en ligne M6 (Propositions seed, quorum par cran, tampon des votes et vote simulé, accueil sous la voûte)
|
||||||
|
Classe : déploie
|
||||||
|
Projet : libreDecision Date : 2026-11-03
|
||||||
|
**État : EXÉCUTÉ (03/11, 06h45-07h04, par orch dev-63 sous mandat ; pipeline 37)** — figé le 03/11/2026 à 06 h 22 sur `94ddb17` (`94ddb175643c8d523a083bbfee3882f22b82f628`) — préparé par
|
||||||
|
l'intégrateur libredecision-d4 ; exécuté par orch (dev-63) sous son mandat (« déploie » et « pousse », du
|
||||||
|
03/11 au 08/11). Relecture indépendante : « 94ddb17 : ok », inscrite le 03/11 à 06 h 42 (prémisse 4).
|
||||||
|
|
||||||
|
## Intention
|
||||||
|
|
||||||
|
Mettre en ligne la revue d'Yvv du 02/11 et ses décisions du 02/11 et du 03/11 (registre) :
|
||||||
|
- **H** : l'accueil sous la voûte, les trois points sous l'arche, le titre centré en deux lignes
|
||||||
|
(`a3c0a76`) ;
|
||||||
|
- **S** : les Propositions seed (I1, E1, E2 ouvertes, au cran 2, ADR 0011), la publication suspendue sans
|
||||||
|
texte en vigueur, le quorum par cran (ADR 0013, `PUT /units/{id}/quorum`), la licence
|
||||||
|
`AGPL-3.0-or-later` (`349c8ed`) ;
|
||||||
|
- **V** : le tampon des votes (ADR 0012), le vote simulé partout tant que la chaîne est fermée, marqué
|
||||||
|
« simulé », qui compte puis s'efface par l'acte « supprime » ; la publication « pas avant des votes
|
||||||
|
prouvés » (`94ddb17`).
|
||||||
|
|
||||||
|
Hors de M6 : T (les articles du seed qui décrivent encore l'ancien plancher C : F1.1, F1.2, N1.1), puis U
|
||||||
|
(le lecteur) en M7. Yvv, à 06 h 07 : « J'ai retiré les propositions. tu peux livrer Decision. »
|
||||||
|
|
||||||
|
## Décisions d'Yvv que l'acte applique
|
||||||
|
|
||||||
|
- Propositions seed au cran 2 ; aucun article en vigueur au départ (02/11).
|
||||||
|
- Quorum par cran 12 · 12 · 50 · 50 · 50 dans Monnaie libre, 1 à l'essai ; l'auteur le change avant le
|
||||||
|
premier vote (02/11) ; sous le quorum, les votants manquants comptent comme « non » (03/11).
|
||||||
|
- Un vote simulé compte, puis s'efface à l'ouverture de la chaîne par un acte « supprime » (02/11).
|
||||||
|
- Q11 (03/11, « Pas avant des votes prouvés ») : un texte entré en vigueur par des votes simulés est en
|
||||||
|
vigueur sur le site, mais ne se publie pas. Règle stricte, confirmée par orch le 03/11 à 06 h 11 : rien
|
||||||
|
ne part tant qu'une entrée simulée ne tient pas sur les seuls votes prouvés.
|
||||||
|
- Q12 (03/11, « Oui, retire-les ») : les propositions d'inertie I1 → 5 et E1 → 5 retirées avant le
|
||||||
|
démarrage du nouveau serveur. **Fait par Yvv à l'écran, annoncé à 06 h 07** ; vu sur la copie de
|
||||||
|
06 h 11 (prémisse 2).
|
||||||
|
|
||||||
|
## Ce qui part
|
||||||
|
|
||||||
|
Version `94ddb17`, qui contient la version en ligne `3f10a0e` : 51 commits, 84 fichiers. Hors du code
|
||||||
|
(vérifié le 03/11 à 06 h 22) : rien dans `docker/`, `.woodpecker/`, `.env` ou `data/`, aucun fichier
|
||||||
|
sensible. Le seed JSON change (bases neuves seulement : un document seedé n'est jamais réinstancié).
|
||||||
|
|
||||||
|
## Ce que l'acte écrit dans la base de production (donnée runtime)
|
||||||
|
|
||||||
|
- **Migrations additives** `0009` (Propositions seed : `proposals.seed_featured`, la table
|
||||||
|
`seed_proposal_amendments`), `0010` (tampon : `ballots.mode` défaut `'chain'`, `ballot_lines.outcome`,
|
||||||
|
l'index `ballots_doc` ; aucune ligne réécrite), `0011` (quorums : `documents.quorums`, `units.quorum`,
|
||||||
|
la table `quorum_amendments`).
|
||||||
|
- **Amendement Propositions seed** (`2026-11-02-i1/e1/e2-proposition-seed`), par document et par article,
|
||||||
|
seulement si personne n'y a touché : la version passe d'en vigueur à ouverte et marquée ;
|
||||||
|
`units.in_force_id` à NULL ; une ligne `revisions` de type `seed_proposal` ; le cran à 2 avec une ligne
|
||||||
|
`inertia_changes`, si personne n'a touché au cran ; une ligne `seed_proposal_amendments` ; dans Monnaie
|
||||||
|
libre, une ligne `outbox`, qui attend.
|
||||||
|
- **Amendement quorums** (`2026-11-03-quorums-par-cran`), une fois par document : `documents.quorums`
|
||||||
|
là où il est NULL ; une ligne `quorum_amendments` ; une ligne `outbox` dans Monnaie libre.
|
||||||
|
- **Le tampon** : la première mise en route n'en écrit rien.
|
||||||
|
- **La publication** : suspendue (aucun article en vigueur, puis « pas avant des votes prouvés »). Le
|
||||||
|
dépôt des textes garde son dernier état.
|
||||||
|
- **Aucun vote, aucune identité, aucune autre proposition n'est touché.**
|
||||||
|
|
||||||
|
**Répétition faite le 03/11 de 06 h 21 à 06 h 22**, avec le serveur de `94ddb17` (binaire de la boucle),
|
||||||
|
sur une copie de la copie de production de 06 h 11 min 12 (santé `3f10a0e8`), publication vers un dépôt nu
|
||||||
|
local (`m6/repetition.sh`, scratchpad de l'intégrateur ; journal `m6/repetition.log`).
|
||||||
|
|
||||||
|
| | Avant | Après |
|
||||||
|
|---|---|---|
|
||||||
|
| Migrations | 1–8 | 1–11 |
|
||||||
|
| Propositions | inertie retirées 3 · ordres en vigueur 16 · textes en vigueur 6 · textes ouverts 64 · titres en vigueur 2 | inertie retirées 3 · ordres en vigueur 16 · textes ouverts 70 · titres en vigueur 2 |
|
||||||
|
| Unités en vigueur | essai 12 · Monnaie libre 12 | essai 9 · Monnaie libre 9 |
|
||||||
|
| I1, E1, E2 (deux groupes) | en vigueur, crans 3, 4, 4 | ouvertes, Propositions seed, cran 2, 2, 2 |
|
||||||
|
| Révisions | 101 | 107 |
|
||||||
|
| Traductions | 144 | 144 |
|
||||||
|
| `inertia_changes` | 4 | 10 |
|
||||||
|
| `seed_proposal_amendments` | absente | 6 lignes, statut et cran « applied » |
|
||||||
|
| `quorum_amendments` / `documents.quorums` | absentes | 2 lignes « applied » ; Monnaie libre `[12,12,50,50,50]`, essai `[1,1,1,1,1]` |
|
||||||
|
| File de publication (Monnaie libre) | 0 en attente, 13 faites | 4 en attente, 13 faites |
|
||||||
|
| Commits poussés vers le dépôt des textes (local) | — | 0 (« publication suspended … waiting=4 ») |
|
||||||
|
| Votes, bulletins, signalements, identités | 0, 0, 0, 1 | 0, 0, 0, 1 |
|
||||||
|
|
||||||
|
`integrity_check` ok, `foreign_key_check` vide, avant et après. Journal : six « seed proposal amendment …
|
||||||
|
status=applied level=applied to_level=2 », deux « quorums written », « publication suspended », aucune
|
||||||
|
ligne ERROR ni WARN. **Un second démarrage ne rejoue rien** (mêmes comptes, la file reste à 4, 0 commit).
|
||||||
|
|
||||||
|
## Effet attendu
|
||||||
|
|
||||||
|
- Les deux groupes : I1, E1 et E2 sont des « Propositions seed », ouvertes, au cran 2 ; aucun article n'est
|
||||||
|
en vigueur ; les modules (titre du document au cran 4, ordre de chaque section au cran 2) restent en
|
||||||
|
vigueur.
|
||||||
|
- Le seuil se lit en quorum : au cran 2, 9 « pour » passent avec 3 « contre » au plus.
|
||||||
|
- **Le vote simulé s'ouvre dans Monnaie libre dès la mise en ligne** (pas de `LD_VOTE_ACCOUNT`) : toute
|
||||||
|
personne qui ouvre le groupe y vote, une personne invitée comprise ; le vote compte aussitôt, marqué
|
||||||
|
« simulé » sous l'article ; 9 « pour » simulés font entrer I1, E1 ou E2 **sur le site**, sans
|
||||||
|
publication. Effet de « simule le vote à tout endroit » et « il compte, puis s'efface » ; orch le porte à
|
||||||
|
son rapport (03/11, 06 h 11). À l'ouverture de la chaîne, l'acte « supprime » (`ld-server
|
||||||
|
discard-simulation`, à blanc, puis `--apply`) écartera ces votes et réexaminera ce qu'ils ont fait entrer.
|
||||||
|
**Cet acte se jouera serveur arrêté** (relecture : `give_back` écrit sans garde, à côté du serveur).
|
||||||
|
- **Un lien d'équipe multiplie les voix simulées** (relecture, vu : un lien vers Monnaie libre ouvert
|
||||||
|
9 fois sous 9 noms donne 9 « pour », et I1 entre sur le site). En production le 03/11 à 06 h 11 :
|
||||||
|
aucun lien d'équipe ; 2 invitations vers Monnaie libre, dont 1 pas encore ouverte (expire le 05/11).
|
||||||
|
**À dire à Yvv** : ne pas créer de lien d'équipe vers Monnaie libre pendant la simulation, ou le
|
||||||
|
borner à quelques ouvertures.
|
||||||
|
- Après un vote simulé, le message bref dit encore « Vote enregistré. » et le panier « N votes
|
||||||
|
enregistrés. » ; sous l'article, c'est bien « simulé ». Repris par U (M7).
|
||||||
|
- L'accueil sous la voûte.
|
||||||
|
|
||||||
|
Irréversibilité : **partielle**. Le code se remet en arrière ; les données changées restent.
|
||||||
|
|
||||||
|
## Avant — prémisses vivantes
|
||||||
|
|
||||||
|
1. La production sert `3f10a0e` : `curl -s https://decision.librodrome.org/api/health` →
|
||||||
|
`"build":"3f10a0e8"` ; `git fetch prod && git rev-parse --short prod/main` → `3f10a0e`.
|
||||||
|
2. Sur une copie fraîche de la base (commande ci-dessous) : aucune proposition d'inertie `open` ; 0 vote,
|
||||||
|
0 bulletin ; I1, E1, E2 en vigueur, aux crans 3, 4, 4, dans les deux groupes. **Lu sur la copie de
|
||||||
|
06 h 11 min 12** : 3 propositions d'inertie, toutes `withdrawn` (I1 → 5, E1 → 5, et une ancienne sur
|
||||||
|
l'ordre de l'introduction) ; 0 vote, 0 bulletin, 0 signalement, 1 identité ; I1, E1, E2 en vigueur,
|
||||||
|
crans 3, 4, 4, dans les deux groupes. À relire sur la seconde copie, juste avant la poussée. Si un de
|
||||||
|
ces articles a bougé, l'amendement le refuse avec sa raison au journal, et l'acte reste valable.
|
||||||
|
3. Sur la même copie : 0 vote, donc aucune proposition n'entre au premier balayage.
|
||||||
|
4. La relecture indépendante de `94ddb17`, inscrite « ok » dans `~/harness/etat/relectures.json` ; aucune
|
||||||
|
autre relecture de ce code en cours. **Faite** : « 94ddb17 : ok », sous-agent neuf, rendue et inscrite le
|
||||||
|
03/11 à 06 h 42 (transcript `~/.claude/projects/-home-yvv-Documents-PROD-DEV-libreDecision/d0a7108b-7f2f-484a-bcb0-de339238199d/subagents/agent-ab9f269c254b56f31.jsonl`).
|
||||||
|
Aucun bloquant. Elle a rejoué la mise en route sur une base faite par `3f10a0e` (écritures exactement
|
||||||
|
celles annoncées, second démarrage sans rejeu, publication suspendue), sondé la formule sur 25 millions
|
||||||
|
de cas (une voix seule ne passe jamais, un « contre » n'aide jamais, `yesNeeded` / `noNeeded` exacts),
|
||||||
|
la route du quorum (bornes, CSRF, auteur, 409 après un vote), les routes de développement (404 sans
|
||||||
|
`LD_DEV_LOGIN`), la simulation et l'acte « supprime » (à blanc : la base inchangée). Ses constats « à
|
||||||
|
reprendre » sont traités dans ce dossier (le retour, le lien d'équipe, l'acte « supprime ») ou
|
||||||
|
renvoyés à U (« Vote enregistré. »).
|
||||||
|
5. `git diff 3f10a0e 94ddb17 -- docker/ .woodpecker/ .env data/` → vide (vérifié à 06 h 22).
|
||||||
|
6. Boucle verte sur `94ddb17` le 03/11 de 06 h 16 à 06 h 21 : fmt, clippy `-D warnings`, 286 tests Rust,
|
||||||
|
svelte-check 0 erreur 0 avertissement, 56 vitest, build, e2e 36 / 36. Image de production construite
|
||||||
|
le 03/11 à 06 h 25 (`docker build -f docker/app.Dockerfile --target production`, sortie 0, les tests
|
||||||
|
rejoués dedans : 56 vitest, tests Rust tous « ok ») ; image de contrôle retirée à 06 h 26.
|
||||||
|
|
||||||
|
## Copie de la base (prise par orch, lecture seule, aucune écriture côté production)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
set -euo pipefail
|
||||||
|
d=/tmp/ld-m6-copie; mkdir -m 700 -p "$d"
|
||||||
|
for f in libredecision.db libredecision.db-wal libredecision.db-shm; do
|
||||||
|
ssh sonic "docker exec lpk-libredecision-app sh -c 'test -f /data/$f && cat /data/$f'" > "$d/$f" </dev/null || rm -f "$d/$f"
|
||||||
|
done
|
||||||
|
chmod 600 "$d"/*
|
||||||
|
date '+%H:%M:%S'; curl -s https://decision.librodrome.org/api/health; echo
|
||||||
|
sqlite3 -readonly "$d/libredecision.db" 'PRAGMA integrity_check; PRAGMA foreign_key_check;'
|
||||||
|
```
|
||||||
|
|
||||||
|
Pas un instantané atomique (la base, puis son journal WAL) : `integrity_check` le dirait, et l'on reprend
|
||||||
|
la copie. Lecture : `m6/counts.sh <db>` (scratchpad de l'intégrateur), en `sqlite3 -readonly`.
|
||||||
|
|
||||||
|
## Commande exacte
|
||||||
|
|
||||||
|
Selon la règle du canon du 02/11, un dossier « déploie » ne liste que la poussée qui met en ligne :
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /home/yvv/Documents/PROD/DEV/libreDecision
|
||||||
|
git push prod 94ddb175643c8d523a083bbfee3882f22b82f628:main
|
||||||
|
```
|
||||||
|
|
||||||
|
(remote `prod` = `gitea@git.open.us.org:EHV/decision.git` ; Woodpecker `.woodpecker/app.yml`, puis
|
||||||
|
`maquette.yml`.)
|
||||||
|
|
||||||
|
## Vérifications après
|
||||||
|
|
||||||
|
- [x] Santé vue de l'extérieur → `"build":"94ddb175"`.
|
||||||
|
- [x] Journaux : `ssh sonic 'docker logs lpk-libredecision-app 2>&1 | grep -E "amendment|quorums written|publication|ERROR|WARN"'`
|
||||||
|
→ six « seed proposal amendment … status=applied level=applied », deux « quorums written »,
|
||||||
|
« publication suspended », aucune erreur.
|
||||||
|
- [x] Redémarrage à froid (`docker compose restart` dans `/opt/libredecision-app`), santé vue de
|
||||||
|
l'extérieur, journaux : aucun amendement rejoué.
|
||||||
|
- [x] Sur une copie fraîche : les comptes « Après » de la répétition, à la ligne près ; `integrity_check`
|
||||||
|
ok, `foreign_key_check` vide ; copie retirée ensuite.
|
||||||
|
- [x] Le dépôt des textes n'a reçu aucun commit : `git ls-remote gitea@git.open.us.org:yvv/decision-depot-textes-reference.git main`
|
||||||
|
→ le même sha qu'avant la poussée (le relever avant).
|
||||||
|
- [ ] À l'écran (Yvv, à son retour) : l'accueil sous la voûte ; l'Acte d'engagement : les Propositions
|
||||||
|
seed, aucun article en vigueur ; voter dans Monnaie libre affiche « simulé ».
|
||||||
|
|
||||||
|
## Retour
|
||||||
|
|
||||||
|
**Avant tout retour vers `3f10a0e`, couper la publication.** `94ddb17` met 4 lignes en file au premier
|
||||||
|
démarrage (`seed_proposal_amend.rs:419`, `quorum_amend.rs:140`) ; `3f10a0e` ne connaît pas l'attente du
|
||||||
|
premier texte et publierait aussitôt un document **sans aucun article** (vu par la relecture : commit de
|
||||||
|
`fr.md`, `en.md`, `es.md` et `document.json` vides, 118 lignes retirées). Le compose prend
|
||||||
|
`${LD_PUBLISH_REMOTE:-…}` : une valeur vide reprend le défaut, il faut donc retirer la ligne.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ssh sonic
|
||||||
|
cd /opt/libredecision-app
|
||||||
|
cp docker-compose.yml docker-compose.yml.m6-avant-retour
|
||||||
|
sed -i '/LD_PUBLISH_REMOTE/d' docker-compose.yml
|
||||||
|
docker tag libredecision-app:previous libredecision-app:latest
|
||||||
|
docker compose up -d --force-recreate
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Répété le 03/11 à 06 h 45** : le serveur de `3f10a0e`, sans `LD_PUBLISH_REMOTE`, sur la base issue de
|
||||||
|
la répétition M6 : démarrage sans erreur, aucune publication (dépôt nu local : 0 commit), la file reste
|
||||||
|
à 4 en attente. Le serveur tolère les migrations qu'il ne connaît pas (`set_ignore_missing(true)`,
|
||||||
|
`lib.rs:100`). La prochaine mise en ligne réécrit `docker-compose.yml` depuis le dépôt (`app.yml`).
|
||||||
|
- **Les données** : pas de retour automatique. `3f10a0e` montrerait I1, E1, E2 comme propositions
|
||||||
|
ouvertes sans la marque, et aucun article en vigueur ; les votes simulés éventuels y compteraient comme
|
||||||
|
des votes du serveur. Un retour du code au-delà de quelques minutes demande un acte de données à part.
|
||||||
|
- **Risque connu, le retour automatique du pipeline** : si le nouveau conteneur échoue après avoir
|
||||||
|
démarré, `.woodpecker/app.yml` relance `previous` (`3f10a0e`) **avec** la publication, qui partirait
|
||||||
|
vide. La répétition et l'image ne montrent aucun échec de démarrage ; si le pipeline échoue quand même,
|
||||||
|
regarder aussitôt le dépôt des textes (`git ls-remote … main`) et, s'il a reçu le document vide, couper
|
||||||
|
la publication comme ci-dessus, puis republier le dernier état par-dessus (acte à part).
|
||||||
|
- **La publication** : en marche avant, rien ne part (suspendue).
|
||||||
|
|
||||||
|
## Exécution
|
||||||
|
|
||||||
|
Acte exécuté le 03/11/2026, 06h45-07h04, par orch (session dev-63, e66db8ca), sous mandat
|
||||||
|
« déploie » (inscrit du 03/11 au 08/11) ; consignes d'Yvv du 03/11 à 06h00 (« livre et déploie tout
|
||||||
|
ce que tu peux ») et à 06h07 (« tu peux livrer Decision »).
|
||||||
|
|
||||||
|
- Dossier relu en entier à 06h45 ; relecture « 94ddb17 : ok » vue inscrite ; aucune autre relecture
|
||||||
|
de ce code en cours (libredecision-d4, PRÊT M6).
|
||||||
|
- Prémisse 1, 06h45:51 : santé `{"build":"3f10a0e8","ok":true}`, `prod/main` = `3f10a0e`.
|
||||||
|
- Seconde copie, 06h46:32 (`/tmp/ld-m6-copie2`, 700/600), santé `3f10a0e8` : `integrity_check` ok,
|
||||||
|
`foreign_key_check` vide. Prémisses 2 et 3 : la colonne « Avant » à la ligne près ; 3 propositions
|
||||||
|
d'inertie, toutes `withdrawn` ; 0 vote, 0 bulletin, 0 signalement, 1 identité ; I1, E1, E2 en vigueur,
|
||||||
|
crans 3, 4, 4, dans les deux groupes ; file 0 en attente, 13 faites.
|
||||||
|
- Dépôt des textes avant : `main` = `9e0f45a9e8d4af356a0c42daa5f4fb9eb9e6dfb3`. Pipelines : 36
|
||||||
|
`success` en tête, rien en cours.
|
||||||
|
- Poussée `git push prod 94ddb175643c8d523a083bbfee3882f22b82f628:main` à 06h46:5x, passée du premier
|
||||||
|
coup (`3f10a0e..94ddb17`).
|
||||||
|
- Pipeline n° 37 : `running` à 06h47:06 ; `app` vert (healthcheck `success`, `retablir` sauté) ;
|
||||||
|
`maquette` verte ; `success` à 07h02:59. L'étape `sbom-publish` échoue dans les deux workflows, comme
|
||||||
|
au pipeline 36 : écart ancien, sans effet sur le déploiement.
|
||||||
|
- Après :
|
||||||
|
- santé `{"build":"94ddb175","ok":true}` ;
|
||||||
|
- journaux : six « seed proposal amendment … status="applied" level="applied" to_level=2 », deux
|
||||||
|
« quorums written » (essai `[1, 1, 1, 1, 1]`, Monnaie libre `[12, 12, 50, 50, 50]`),
|
||||||
|
« publication suspended … waiting=4 », aucune ligne ERROR ni WARN ;
|
||||||
|
- redémarrage à froid (`docker compose restart` dans `/opt/libredecision-app`) à 07h03:07 :
|
||||||
|
« listening … build=94ddb175 », « publication suspended … waiting=4 », aucun amendement rejoué ;
|
||||||
|
santé `94ddb175` ;
|
||||||
|
- copie fraîche, 07h03:31 (`/tmp/ld-m6-apres`) : la colonne « Après » de la répétition à la ligne
|
||||||
|
près. Migrations 1–11 ; inertie retirées 3, ordres en vigueur 16, textes ouverts 70, titres en
|
||||||
|
vigueur 2 ; unités en vigueur 9 et 9 ; I1, E1, E2 ouvertes au cran 2 dans les deux groupes ;
|
||||||
|
révisions 107 ; traductions 144 ; `inertia_changes` 10 ; 6 `seed_proposal_amendments` « applied » ;
|
||||||
|
2 `quorum_amendments` « applied » ; file 4 en attente, 13 faites ; 0, 0, 0, 1 ; `integrity_check`
|
||||||
|
ok, `foreign_key_check` vide ;
|
||||||
|
- dépôt des textes : `main` = `9e0f45a9e8d4af356a0c42daa5f4fb9eb9e6dfb3`, inchangé : aucun commit.
|
||||||
|
- `main` local avancé sur `94ddb17` (`git fetch . 94ddb17…:main`, depuis `fa8e8dc`).
|
||||||
|
- Les trois copies de la base (`/tmp/ld-m6-copie` de 06h11, `/tmp/ld-m6-copie2`, `/tmp/ld-m6-apres`)
|
||||||
|
retirées à 07h03:43. Les copies de répétition de l'intégrateur sont à retirer par lui.
|
||||||
|
- Reste à l'écran, pour Yvv à son retour : l'accueil sous la voûte ; l'Acte d'engagement (les
|
||||||
|
Propositions seed, aucun article en vigueur) ; un vote dans Monnaie libre affiche « simulé ».
|
||||||
|
- Image de retour : `libredecision-app:previous` (`3f10a0e`), avec la coupure de la publication
|
||||||
|
d'abord (§ Retour).
|
||||||
@@ -7,6 +7,15 @@
|
|||||||
//! proposal instead of a text in force (ADR 0011): where an article has no text
|
//! proposal instead of a text in force (ADR 0011): where an article has no text
|
||||||
//! in force, `Version::InForce` examines its seed proposal (a new base: present).
|
//! in force, `Version::InForce` examines its seed proposal (a new base: present).
|
||||||
//!
|
//!
|
||||||
|
//! Since 2026-11-03 (lot T), a text may be amended twice: F1.1, F1.2 and N1.1 say
|
||||||
|
//! the quorum by level (ADR 0013) instead of the floor C, and F1.2 and N1.1 had
|
||||||
|
//! been rewritten by the amendments of 02/11. An amendment that `follows` an
|
||||||
|
//! earlier one replaces the text that one wrote: it admits, besides the seed's
|
||||||
|
//! own revision 1, the revision 2 whose history is « initial » then « amendment »
|
||||||
|
//! and on which that earlier amendment, and it alone, was applied — nothing else.
|
||||||
|
//! The earlier one is then `superseded_by` it: the seed no longer gives its text,
|
||||||
|
//! so it decides nothing more, without an error (the decisions already taken stay).
|
||||||
|
//!
|
||||||
//! A seeded document is never instantiated again: once in the database, it is
|
//! A seeded document is never instantiated again: once in the database, it is
|
||||||
//! runtime data. When the seed's text of an article changes (Yvv, 2026-10-30:
|
//! runtime data. When the seed's text of an article changes (Yvv, 2026-10-30:
|
||||||
//! the Préambule becomes « Raison d'être »), new bases get it from the seed;
|
//! the Préambule becomes « Raison d'être »), new bases get it from the seed;
|
||||||
@@ -14,9 +23,11 @@
|
|||||||
//! nobody has touched the article**:
|
//! nobody has touched the article**:
|
||||||
//!
|
//!
|
||||||
//! - its version in force is the seed's own, a *seed a minima* (never voted
|
//! - its version in force is the seed's own, a *seed a minima* (never voted
|
||||||
//! into force, never confirmed), at revision 1;
|
//! into force, never confirmed), at revision 1 — or 2, after the amendment it
|
||||||
//! - nobody ever voted on it, corrected it, edited one of its translations or
|
//! follows;
|
||||||
//! named it in a contradiction signal;
|
//! - nobody ever voted on it (a vote, or a line of a ballot of the buffer, pending
|
||||||
|
//! or proven, whatever became of it: ADR 0012), corrected it, edited one of its
|
||||||
|
//! translations or named it in a contradiction signal (an objection);
|
||||||
//! - its text is exactly the one the amendment replaces (fingerprint).
|
//! - its text is exactly the one the amendment replaces (fingerprint).
|
||||||
//!
|
//!
|
||||||
//! Each document is decided once, at the first start that knows the
|
//! Each document is decided once, at the first start that knows the
|
||||||
@@ -58,6 +69,12 @@ pub struct Amendment {
|
|||||||
pub to: &'static str,
|
pub to: &'static str,
|
||||||
/// Reason given to the publication queue, and so to the commit of the texts.
|
/// Reason given to the publication queue, and so to the commit of the texts.
|
||||||
pub reason: &'static str,
|
pub reason: &'static str,
|
||||||
|
/// The earlier amendment of the same text whose text this one replaces, if any:
|
||||||
|
/// its revision 2 is then admitted (see the module).
|
||||||
|
pub follows: Option<&'static str>,
|
||||||
|
/// The later amendment whose text the seed gives instead, if any: this one then
|
||||||
|
/// decides nothing more, without an error.
|
||||||
|
pub superseded_by: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const AMENDMENTS: &[Amendment] = &[
|
pub const AMENDMENTS: &[Amendment] = &[
|
||||||
@@ -69,6 +86,8 @@ pub const AMENDMENTS: &[Amendment] = &[
|
|||||||
from: "ac17693c61b678ae48f5386f3d528a78385a62f3e2bb12a802a21f45c762b375",
|
from: "ac17693c61b678ae48f5386f3d528a78385a62f3e2bb12a802a21f45c762b375",
|
||||||
to: "a7095d70602a0fc60d0904e9ffbce6d8520914ce3cb424d8f75e48bf6cfdf41a",
|
to: "a7095d70602a0fc60d0904e9ffbce6d8520914ce3cb424d8f75e48bf6cfdf41a",
|
||||||
reason: "texte de départ remplacé : I1",
|
reason: "texte de départ remplacé : I1",
|
||||||
|
follows: None,
|
||||||
|
superseded_by: None,
|
||||||
},
|
},
|
||||||
// The rule articles, open proposals of the seed, after the rules applied (Yvv, 2026-11-02).
|
// The rule articles, open proposals of the seed, after the rules applied (Yvv, 2026-11-02).
|
||||||
Amendment {
|
Amendment {
|
||||||
@@ -79,6 +98,8 @@ pub const AMENDMENTS: &[Amendment] = &[
|
|||||||
from: "044c65d6b97e4278184cb1acfe6fa2227ddfe91465c717d8a81f52092314fae6",
|
from: "044c65d6b97e4278184cb1acfe6fa2227ddfe91465c717d8a81f52092314fae6",
|
||||||
to: "b24ff4d1c95cfbaae0231885009ee3d600957a3ce498bede80b29bceca651a60",
|
to: "b24ff4d1c95cfbaae0231885009ee3d600957a3ce498bede80b29bceca651a60",
|
||||||
reason: "proposition de départ réécrite : F1.2",
|
reason: "proposition de départ réécrite : F1.2",
|
||||||
|
follows: None,
|
||||||
|
superseded_by: Some("2026-11-03-f1-2-quorum"),
|
||||||
},
|
},
|
||||||
Amendment {
|
Amendment {
|
||||||
id: "2026-11-02-n1-regles",
|
id: "2026-11-02-n1-regles",
|
||||||
@@ -88,6 +109,8 @@ pub const AMENDMENTS: &[Amendment] = &[
|
|||||||
from: "eed13441afae8d33eba37545163037ccf89b0801d84072ef55290575a1410142",
|
from: "eed13441afae8d33eba37545163037ccf89b0801d84072ef55290575a1410142",
|
||||||
to: "c1facdfd0c0f3aaa94b74192e146557558fe88f66304dc6d21279714d7fb727e",
|
to: "c1facdfd0c0f3aaa94b74192e146557558fe88f66304dc6d21279714d7fb727e",
|
||||||
reason: "proposition de départ réécrite : N1",
|
reason: "proposition de départ réécrite : N1",
|
||||||
|
follows: None,
|
||||||
|
superseded_by: None,
|
||||||
},
|
},
|
||||||
Amendment {
|
Amendment {
|
||||||
id: "2026-11-02-n1-1-regles",
|
id: "2026-11-02-n1-1-regles",
|
||||||
@@ -97,6 +120,8 @@ pub const AMENDMENTS: &[Amendment] = &[
|
|||||||
from: "d5d06c5ae5f3a1c1b08e3c3721b2b09c3dcfd528c11d16d3368d6e14d8942bee",
|
from: "d5d06c5ae5f3a1c1b08e3c3721b2b09c3dcfd528c11d16d3368d6e14d8942bee",
|
||||||
to: "865ed24d5eb18745a8c35dc727247219c449e078d39b6a18ec2fdcfd4f8289a0",
|
to: "865ed24d5eb18745a8c35dc727247219c449e078d39b6a18ec2fdcfd4f8289a0",
|
||||||
reason: "proposition de départ réécrite : N1.1",
|
reason: "proposition de départ réécrite : N1.1",
|
||||||
|
follows: None,
|
||||||
|
superseded_by: Some("2026-11-03-n1-1-quorum"),
|
||||||
},
|
},
|
||||||
Amendment {
|
Amendment {
|
||||||
id: "2026-11-02-o1-regles",
|
id: "2026-11-02-o1-regles",
|
||||||
@@ -106,6 +131,44 @@ pub const AMENDMENTS: &[Amendment] = &[
|
|||||||
from: "40ab4c06a9e374bded6023b9dffbee5736fc4000d422c5a819f35cdb00b02642",
|
from: "40ab4c06a9e374bded6023b9dffbee5736fc4000d422c5a819f35cdb00b02642",
|
||||||
to: "4173738055af10fceb74bb6e954bdfefd7ab364480d49afbc92ea5f68825c537",
|
to: "4173738055af10fceb74bb6e954bdfefd7ab364480d49afbc92ea5f68825c537",
|
||||||
reason: "proposition de départ réécrite : O1",
|
reason: "proposition de départ réécrite : O1",
|
||||||
|
follows: None,
|
||||||
|
superseded_by: None,
|
||||||
|
},
|
||||||
|
// The articles that still described the floor C, after the quorum by level (ADR 0013;
|
||||||
|
// Yvv, 2026-11-02 and 03; lot T). F1.1 was never amended; F1.2 and N1.1 replace the
|
||||||
|
// text the amendments of 02/11 wrote.
|
||||||
|
Amendment {
|
||||||
|
id: "2026-11-03-f1-1-quorum",
|
||||||
|
doc: "acte-engagement-certification",
|
||||||
|
unit: "F1.1",
|
||||||
|
version: Version::Open("1.0.0"),
|
||||||
|
from: "258980be190192fe3c7b32f6319584b682621d01329d880b7056c28c84e9db0a",
|
||||||
|
to: "ca6d3c74a3e6de22ebf55eb0cdd6465dbe22d69cd86aae89ac335b960671b4fa",
|
||||||
|
reason: "proposition de départ réécrite : F1.1, le quorum par cran",
|
||||||
|
follows: None,
|
||||||
|
superseded_by: None,
|
||||||
|
},
|
||||||
|
Amendment {
|
||||||
|
id: "2026-11-03-f1-2-quorum",
|
||||||
|
doc: "acte-engagement-certification",
|
||||||
|
unit: "F1.2",
|
||||||
|
version: Version::Open("1.0.0"),
|
||||||
|
from: "b24ff4d1c95cfbaae0231885009ee3d600957a3ce498bede80b29bceca651a60",
|
||||||
|
to: "66a9bc125c78120a76a81e8b53c427f4d2d5895581cb4ed4ade1aaab740944a9",
|
||||||
|
reason: "proposition de départ réécrite : F1.2, le quorum par cran",
|
||||||
|
follows: Some("2026-11-02-f1-2-regles"),
|
||||||
|
superseded_by: None,
|
||||||
|
},
|
||||||
|
Amendment {
|
||||||
|
id: "2026-11-03-n1-1-quorum",
|
||||||
|
doc: "acte-engagement-certification",
|
||||||
|
unit: "N1.1",
|
||||||
|
version: Version::Open("1.0.0"),
|
||||||
|
from: "865ed24d5eb18745a8c35dc727247219c449e078d39b6a18ec2fdcfd4f8289a0",
|
||||||
|
to: "1bdd26a63cadb4529f5347863ced83a823439f53247b6aea8e703301bab3de48",
|
||||||
|
reason: "proposition de départ réécrite : N1.1, le quorum par cran",
|
||||||
|
follows: Some("2026-11-02-n1-1-regles"),
|
||||||
|
superseded_by: None,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -125,6 +188,10 @@ pub struct Facts {
|
|||||||
pub revision: i64,
|
pub revision: i64,
|
||||||
/// Votes cast on it, whatever their revision.
|
/// Votes cast on it, whatever their revision.
|
||||||
pub votes: i64,
|
pub votes: i64,
|
||||||
|
/// Lines of ballots of the buffer naming it, pending or proven, whatever became of them.
|
||||||
|
pub ballots: i64,
|
||||||
|
/// The seed amendments applied on it, in order.
|
||||||
|
pub amendments: Vec<String>,
|
||||||
/// Kinds of its revisions, in order.
|
/// Kinds of its revisions, in order.
|
||||||
pub revisions: Vec<String>,
|
pub revisions: Vec<String>,
|
||||||
/// Translations written or corrected by a member.
|
/// Translations written or corrected by a member.
|
||||||
@@ -155,17 +222,22 @@ pub fn verdict(facts: Option<&Facts>, a: &Amendment) -> Verdict {
|
|||||||
if f.fingerprint == a.to {
|
if f.fingerprint == a.to {
|
||||||
return Verdict::Present;
|
return Verdict::Present;
|
||||||
}
|
}
|
||||||
|
// The text the amendment follows: written by it, and by it alone, at revision 2.
|
||||||
|
let after_earlier = a.follows.is_some_and(|id| f.revision == 2 && f.amendments == [id]);
|
||||||
|
let history: &[&str] = if after_earlier { &["initial", "amendment"] } else { &["initial"] };
|
||||||
let refusal = if f.origin != "seed" {
|
let refusal = if f.origin != "seed" {
|
||||||
"not_a_seed"
|
"not_a_seed"
|
||||||
} else if f.entered {
|
} else if f.entered {
|
||||||
"voted_into_force"
|
"voted_into_force"
|
||||||
} else if f.confirmed {
|
} else if f.confirmed {
|
||||||
"confirmed"
|
"confirmed"
|
||||||
} else if f.revision != 1 {
|
} else if f.revision != 1 && !after_earlier {
|
||||||
"revised"
|
"revised"
|
||||||
} else if f.votes > 0 {
|
} else if f.votes > 0 {
|
||||||
"votes"
|
"votes"
|
||||||
} else if f.revisions != ["initial"] || f.typos > 0 {
|
} else if f.ballots > 0 {
|
||||||
|
"ballots"
|
||||||
|
} else if f.revisions != history || f.typos > 0 {
|
||||||
"corrected"
|
"corrected"
|
||||||
} else if f.member_translations > 0 {
|
} else if f.member_translations > 0 {
|
||||||
"translation_edited"
|
"translation_edited"
|
||||||
@@ -234,6 +306,15 @@ fn target_of<'a>(seeds: &'a [SeedDoc], a: &Amendment) -> Option<(&'a SeedDoc, &'
|
|||||||
let text = version.and_then(|v| Some((v.title.get(src)?, v.body.get(src)?)));
|
let text = version.and_then(|v| Some((v.title.get(src)?, v.body.get(src)?)));
|
||||||
match (version, text) {
|
match (version, text) {
|
||||||
(Some(v), Some((title, body))) if fingerprint(title, body) == a.to => Some((seed, v)),
|
(Some(v), Some((title, body))) if fingerprint(title, body) == a.to => Some((seed, v)),
|
||||||
|
_ if a.superseded_by.is_some() => {
|
||||||
|
tracing::debug!(
|
||||||
|
amendment = a.id,
|
||||||
|
unit = a.unit,
|
||||||
|
superseded_by = a.superseded_by,
|
||||||
|
"seed amendment superseded: nothing decided"
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
amendment = a.id,
|
amendment = a.id,
|
||||||
@@ -294,12 +375,25 @@ pub(crate) async fn facts(
|
|||||||
.bind(proposal_id)
|
.bind(proposal_id)
|
||||||
.fetch_one(&mut *conn)
|
.fetch_one(&mut *conn)
|
||||||
.await?;
|
.await?;
|
||||||
|
// Every line of a ballot, chain or simulated, whatever its fate: a vote was cast.
|
||||||
|
let ballots: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM ballot_lines WHERE proposal_id = ?")
|
||||||
|
.bind(proposal_id)
|
||||||
|
.fetch_one(&mut *conn)
|
||||||
|
.await?;
|
||||||
|
let amendments: Vec<String> = sqlx::query_scalar(
|
||||||
|
"SELECT amendment_id FROM seed_amendments WHERE proposal_id = ? AND outcome = 'applied' ORDER BY at, rowid",
|
||||||
|
)
|
||||||
|
.bind(proposal_id)
|
||||||
|
.fetch_all(&mut *conn)
|
||||||
|
.await?;
|
||||||
Ok(Some(Facts {
|
Ok(Some(Facts {
|
||||||
origin: p.origin,
|
origin: p.origin,
|
||||||
entered: p.entered_at.is_some(),
|
entered: p.entered_at.is_some(),
|
||||||
confirmed: p.confirmed_at.is_some(),
|
confirmed: p.confirmed_at.is_some(),
|
||||||
revision: p.revision,
|
revision: p.revision,
|
||||||
votes,
|
votes,
|
||||||
|
ballots,
|
||||||
|
amendments,
|
||||||
revisions,
|
revisions,
|
||||||
member_translations,
|
member_translations,
|
||||||
signals,
|
signals,
|
||||||
@@ -358,6 +452,7 @@ async fn decide(
|
|||||||
};
|
};
|
||||||
let mut v = verdict(found.as_ref(), a);
|
let mut v = verdict(found.as_ref(), a);
|
||||||
let now = util::now();
|
let now = util::now();
|
||||||
|
let judged = found.as_ref().map_or(1, |f| f.revision);
|
||||||
if let (Verdict::Apply, Some((pid, status))) = (v, &examined) {
|
if let (Verdict::Apply, Some((pid, status))) = (v, &examined) {
|
||||||
let src = seed.source_lang.as_str();
|
let src = seed.source_lang.as_str();
|
||||||
let pick = |m: &util::LangMap| m.get(src).cloned().unwrap_or_default();
|
let pick = |m: &util::LangMap| m.get(src).cloned().unwrap_or_default();
|
||||||
@@ -366,21 +461,23 @@ async fn decide(
|
|||||||
let done = sqlx::query(
|
let done = sqlx::query(
|
||||||
"UPDATE proposals SET title = ?, body = ?, revision = revision + 1, updated_at = ?
|
"UPDATE proposals SET title = ?, body = ?, revision = revision + 1, updated_at = ?
|
||||||
WHERE id = ? AND status = ? AND origin = 'seed' AND entered_at IS NULL
|
WHERE id = ? AND status = ? AND origin = 'seed' AND entered_at IS NULL
|
||||||
AND confirmed_at IS NULL AND revision = 1",
|
AND confirmed_at IS NULL AND revision = ?",
|
||||||
)
|
)
|
||||||
.bind(&title)
|
.bind(&title)
|
||||||
.bind(&body)
|
.bind(&body)
|
||||||
.bind(&now)
|
.bind(&now)
|
||||||
.bind(pid)
|
.bind(pid)
|
||||||
.bind(*status)
|
.bind(*status)
|
||||||
|
.bind(judged)
|
||||||
.execute(&mut *tx)
|
.execute(&mut *tx)
|
||||||
.await?;
|
.await?;
|
||||||
if done.rows_affected() == 1 {
|
if done.rows_affected() == 1 {
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
"INSERT INTO revisions (proposal_id, revision, kind, title, body, author_id, created_at)
|
"INSERT INTO revisions (proposal_id, revision, kind, title, body, author_id, created_at)
|
||||||
VALUES (?, 2, 'amendment', ?, ?, NULL, ?)",
|
VALUES (?, ?, 'amendment', ?, ?, NULL, ?)",
|
||||||
)
|
)
|
||||||
.bind(pid)
|
.bind(pid)
|
||||||
|
.bind(judged + 1)
|
||||||
.bind(&title)
|
.bind(&title)
|
||||||
.bind(&body)
|
.bind(&body)
|
||||||
.bind(&now)
|
.bind(&now)
|
||||||
@@ -451,6 +548,8 @@ mod tests {
|
|||||||
from: "old",
|
from: "old",
|
||||||
to: "new",
|
to: "new",
|
||||||
reason: "texte de départ remplacé : I1",
|
reason: "texte de départ remplacé : I1",
|
||||||
|
follows: None,
|
||||||
|
superseded_by: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn intact() -> Facts {
|
fn intact() -> Facts {
|
||||||
@@ -497,5 +596,54 @@ mod tests {
|
|||||||
let open = Amendment { version: Version::Open("1.0.0"), ..A };
|
let open = Amendment { version: Version::Open("1.0.0"), ..A };
|
||||||
assert_eq!(verdict(None, &open), Verdict::Refuse("no_open_version"));
|
assert_eq!(verdict(None, &open), Verdict::Refuse("no_open_version"));
|
||||||
assert_eq!(verdict(Some(&intact()), &open), Verdict::Apply);
|
assert_eq!(verdict(Some(&intact()), &open), Verdict::Apply);
|
||||||
|
// A ballot of the buffer names it, pending or proven, whatever became of it (ADR 0012).
|
||||||
|
assert_eq!(verdict(Some(&Facts { ballots: 1, ..intact() }), &A), Verdict::Refuse("ballots"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The text an earlier amendment wrote, as production holds F1.2 and N1.1 since M5.
|
||||||
|
fn amended_by(earlier: &str) -> Facts {
|
||||||
|
Facts {
|
||||||
|
revision: 2,
|
||||||
|
revisions: vec!["initial".into(), "amendment".into()],
|
||||||
|
amendments: vec![earlier.into()],
|
||||||
|
..intact()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_second_amendment_admits_the_text_the_first_wrote_and_nothing_else() {
|
||||||
|
// Lot T (2026-11-03): F1.2 and N1.1 were amended by M5 (revision 2); the quorum
|
||||||
|
// texts replace them, after that amendment only.
|
||||||
|
let second = Amendment { version: Version::Open("1.0.0"), follows: Some("m5"), ..A };
|
||||||
|
assert_eq!(verdict(Some(&amended_by("m5")), &second), Verdict::Apply);
|
||||||
|
// A base born with the text of M5: its revision 1, as the seed made it.
|
||||||
|
assert_eq!(verdict(Some(&intact()), &second), Verdict::Apply);
|
||||||
|
// Not the amendment it follows, or none: a revision 2 of something else.
|
||||||
|
assert_eq!(verdict(Some(&amended_by("other")), &second), Verdict::Refuse("revised"));
|
||||||
|
let typo = Facts { revisions: vec!["initial".into(), "typo".into()], amendments: vec![], ..amended_by("m5") };
|
||||||
|
assert_eq!(verdict(Some(&typo), &second), Verdict::Refuse("revised"));
|
||||||
|
// A history that is not « initial then amendment ».
|
||||||
|
let odd = Facts { revisions: vec!["initial".into(), "substantive".into()], ..amended_by("m5") };
|
||||||
|
assert_eq!(verdict(Some(&odd), &second), Verdict::Refuse("corrected"));
|
||||||
|
// Further on, nothing is admitted.
|
||||||
|
let third = Facts {
|
||||||
|
revision: 3,
|
||||||
|
revisions: vec!["initial".into(), "amendment".into(), "typo".into()],
|
||||||
|
..amended_by("m5")
|
||||||
|
};
|
||||||
|
assert_eq!(verdict(Some(&third), &second), Verdict::Refuse("revised"));
|
||||||
|
// The same traces of a person keep it.
|
||||||
|
for (f, why) in [
|
||||||
|
(Facts { votes: 1, ..amended_by("m5") }, "votes"),
|
||||||
|
(Facts { ballots: 2, ..amended_by("m5") }, "ballots"),
|
||||||
|
(Facts { typos: 1, ..amended_by("m5") }, "corrected"),
|
||||||
|
(Facts { member_translations: 1, ..amended_by("m5") }, "translation_edited"),
|
||||||
|
(Facts { signals: 1, ..amended_by("m5") }, "signalled"),
|
||||||
|
(Facts { fingerprint: "other".into(), ..amended_by("m5") }, "text_differs"),
|
||||||
|
] {
|
||||||
|
assert_eq!(verdict(Some(&f), &second), Verdict::Refuse(why), "{f:?}");
|
||||||
|
}
|
||||||
|
// An amendment that follows none never admits a revision 2.
|
||||||
|
assert_eq!(verdict(Some(&amended_by("m5")), &A), Verdict::Refuse("revised"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ use crate::util;
|
|||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
const REFRESH: Duration = Duration::from_secs(6 * 3600);
|
/// W is read again this often.
|
||||||
|
pub const REFRESH: Duration = Duration::from_secs(6 * 3600);
|
||||||
const PLAUSIBLE: std::ops::RangeInclusive<u32> = 100..=10_000_000;
|
const PLAUSIBLE: std::ops::RangeInclusive<u32> = 100..=10_000_000;
|
||||||
|
|
||||||
fn twox_128(data: &[u8]) -> [u8; 16] {
|
fn twox_128(data: &[u8]) -> [u8; 16] {
|
||||||
|
|||||||
@@ -353,9 +353,13 @@ async fn the_production_base_turns_its_untouched_texts_into_seed_proposals_once(
|
|||||||
kept.push(v);
|
kept.push(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// The articles of the quorum (F1.1, F1.2, N1.1) are amended at the same start, by the
|
||||||
|
// seed amendments of lot T (`tests/seed_quorum_texts.rs`): left out here.
|
||||||
let translations = |state: AppState| async move {
|
let translations = |state: AppState| async move {
|
||||||
sqlx::query_as::<_, (String, String, String, String, String)>(
|
sqlx::query_as::<_, (String, String, String, String, String)>(
|
||||||
"SELECT proposal_id, lang, title, body, source_hash FROM translations ORDER BY proposal_id, lang",
|
"SELECT t.proposal_id, t.lang, t.title, t.body, t.source_hash FROM translations t
|
||||||
|
JOIN proposals p ON p.id = t.proposal_id JOIN units u ON u.id = p.unit_id
|
||||||
|
WHERE u.code NOT IN ('F1.1', 'F1.2', 'N1.1') ORDER BY t.proposal_id, t.lang",
|
||||||
)
|
)
|
||||||
.fetch_all(&state.db)
|
.fetch_all(&state.db)
|
||||||
.await
|
.await
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,10 @@
|
|||||||
//! The four rule articles of the seed rewritten after the rules applied (Yvv,
|
//! The four rule articles of the seed rewritten after the rules applied (Yvv,
|
||||||
//! 2026-11-02): F1.2, N1, N1.1 and O1 are open proposals at zero votes, not texts
|
//! 2026-11-02): F1.2, N1, N1.1 and O1 are open proposals at zero votes, not texts
|
||||||
//! in force; a guarded seed amendment replaces them where nobody touched them.
|
//! in force; a guarded seed amendment replaces them where nobody touched them.
|
||||||
|
//!
|
||||||
|
//! Played with the seed M5 shipped (3f10a0e, the fixture of production): since lot T
|
||||||
|
//! (2026-11-03), today's seed gives F1.2 and N1.1 the texts of the quorum, and their
|
||||||
|
//! amendments of 02/11 are superseded (`tests/seed_quorum_texts.rs`).
|
||||||
|
|
||||||
use ld_server::seed_amend::{self, AMENDMENTS, Version};
|
use ld_server::seed_amend::{self, AMENDMENTS, Version};
|
||||||
use ld_server::{AppState, Config, open_db, seed};
|
use ld_server::{AppState, Config, open_db, seed};
|
||||||
@@ -21,6 +25,15 @@ fn today_seed() -> Value {
|
|||||||
read(Config::for_tests().seed_dir.join("acte-engagement-certification.json"))
|
read(Config::for_tests().seed_dir.join("acte-engagement-certification.json"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The seed M5 shipped: production's since 2026-11-02.
|
||||||
|
fn m5_dir() -> std::path::PathBuf {
|
||||||
|
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/seed-production-2026-11-02")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn m5_seed() -> Value {
|
||||||
|
read(m5_dir().join("acte-engagement-certification.json"))
|
||||||
|
}
|
||||||
|
|
||||||
fn version<'a>(seed: &'a Value, code: &str) -> &'a Value {
|
fn version<'a>(seed: &'a Value, code: &str) -> &'a Value {
|
||||||
let u = seed["units"].as_array().expect("units").iter().find(|u| u["code"] == code).expect("unit");
|
let u = seed["units"].as_array().expect("units").iter().find(|u| u["code"] == code).expect("unit");
|
||||||
&u["versions"][0]
|
&u["versions"][0]
|
||||||
@@ -30,12 +43,12 @@ fn fr(v: &Value) -> (&str, &str) {
|
|||||||
(v["title"]["fr"].as_str().expect("title"), v["body"]["fr"].as_str().expect("body"))
|
(v["title"]["fr"].as_str().expect("title"), v["body"]["fr"].as_str().expect("body"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Today's seed with the four rule articles of before.
|
/// The seed of M5 with the four rule articles of before.
|
||||||
fn seed_dir_before(tag: &str) -> std::path::PathBuf {
|
fn seed_dir_before(tag: &str) -> std::path::PathBuf {
|
||||||
let dir = std::env::temp_dir().join(format!("ld-seed-rules-{tag}-{}", std::process::id()));
|
let dir = std::env::temp_dir().join(format!("ld-seed-rules-{tag}-{}", std::process::id()));
|
||||||
let _ = std::fs::remove_dir_all(&dir);
|
let _ = std::fs::remove_dir_all(&dir);
|
||||||
std::fs::create_dir_all(&dir).expect("dir");
|
std::fs::create_dir_all(&dir).expect("dir");
|
||||||
let mut seed = today_seed();
|
let mut seed = m5_seed();
|
||||||
let old = before();
|
let old = before();
|
||||||
for u in seed["units"].as_array_mut().expect("units") {
|
for u in seed["units"].as_array_mut().expect("units") {
|
||||||
let code = u["code"].as_str().expect("code").to_owned();
|
let code = u["code"].as_str().expect("code").to_owned();
|
||||||
@@ -59,9 +72,11 @@ async fn base_before(tag: &str) -> AppState {
|
|||||||
state
|
state
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The same base, started again with today's seed and its amendments.
|
/// The same base, started again with the seed of M5 and its amendments.
|
||||||
async fn restart(before: &AppState) -> AppState {
|
async fn restart(before: &AppState) -> AppState {
|
||||||
let state = AppState::new(Config::for_tests(), before.db.clone()).await.expect("state");
|
let mut cfg = Config::for_tests();
|
||||||
|
cfg.seed_dir = m5_dir();
|
||||||
|
let state = AppState::new(cfg, before.db.clone()).await.expect("state");
|
||||||
seed::ensure(&state).await.expect("seed");
|
seed::ensure(&state).await.expect("seed");
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
@@ -92,19 +107,29 @@ async fn decisions(state: &AppState) -> Vec<(String, String, String, Option<Stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_shipped_amendments_go_from_the_texts_of_before_to_the_seed_of_today() {
|
fn the_shipped_amendments_go_from_the_texts_of_before_to_the_seed_of_m5() {
|
||||||
let (old, today) = (before(), today_seed());
|
let (old, m5, today) = (before(), m5_seed(), today_seed());
|
||||||
for code in CODES {
|
for code in CODES {
|
||||||
let a = AMENDMENTS.iter().find(|a| a.unit == code).unwrap_or_else(|| panic!("no amendment for {code}"));
|
let a = AMENDMENTS.iter().find(|a| a.unit == code).unwrap_or_else(|| panic!("no amendment for {code}"));
|
||||||
assert_eq!(a.doc, "acte-engagement-certification");
|
assert_eq!(a.doc, "acte-engagement-certification");
|
||||||
assert_eq!(a.version, Version::Open("1.0.0"), "{code}: the open proposal of the seed");
|
assert_eq!(a.version, Version::Open("1.0.0"), "{code}: the open proposal of the seed");
|
||||||
let (t, b) = fr(&old["units"][code]);
|
let (t, b) = fr(&old["units"][code]);
|
||||||
assert_eq!(seed_amend::fingerprint(t, b), a.from, "{code}: the text production holds");
|
assert_eq!(seed_amend::fingerprint(t, b), a.from, "{code}: the text production holds");
|
||||||
let v = version(&today, code);
|
let v = version(&m5, code);
|
||||||
assert_eq!((v["status"].as_str(), v["label"].as_str()), (Some("open"), Some("1.0.0")), "{code}");
|
assert_eq!((v["status"].as_str(), v["label"].as_str()), (Some("open"), Some("1.0.0")), "{code}");
|
||||||
let (t, b) = fr(v);
|
let (t, b) = fr(v);
|
||||||
assert_eq!(seed_amend::fingerprint(t, b), a.to, "{code}: the text of the seed today");
|
assert_eq!(seed_amend::fingerprint(t, b), a.to, "{code}: the text of the seed of M5");
|
||||||
assert_ne!(a.from, a.to, "{code}");
|
assert_ne!(a.from, a.to, "{code}");
|
||||||
|
// Today's seed gives it, or another text whose amendment follows this one.
|
||||||
|
let (t, b) = fr(version(&today, code));
|
||||||
|
let now = seed_amend::fingerprint(t, b);
|
||||||
|
match a.superseded_by {
|
||||||
|
None => assert_eq!(now, a.to, "{code}: still the text of today"),
|
||||||
|
Some(later) => {
|
||||||
|
let l = AMENDMENTS.iter().find(|l| l.id == later).expect("the later amendment");
|
||||||
|
assert_eq!((l.unit, l.follows, l.from, l.to), (code, Some(a.id), a.to, now.as_str()), "{code}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +148,7 @@ async fn untouched_rule_articles_are_replaced_once_and_a_vote_keeps_its_text() {
|
|||||||
.await
|
.await
|
||||||
.expect("vote");
|
.expect("vote");
|
||||||
let state = restart(&base).await;
|
let state = restart(&base).await;
|
||||||
let (old, today) = (before(), today_seed());
|
let (old, today) = (before(), m5_seed());
|
||||||
for group in ["essai", "monnaie-libre"] {
|
for group in ["essai", "monnaie-libre"] {
|
||||||
for code in CODES {
|
for code in CODES {
|
||||||
let (id, title, body, revision) = proposal(&state, group, code).await;
|
let (id, title, body, revision) = proposal(&state, group, code).await;
|
||||||
@@ -202,6 +227,9 @@ async fn a_new_base_is_born_with_the_rules_of_today() {
|
|||||||
assert_eq!(((title.as_str(), body.as_str()), revision), (fr(version(&today, code)), 1), "{group} {code}");
|
assert_eq!(((title.as_str(), body.as_str()), revision), (fr(version(&today, code)), 1), "{group} {code}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert!(decisions(&state).await.iter().all(|(_, _, o, _)| o == "present"));
|
// N1 and O1: present. F1.2 and N1.1: their amendments, superseded, decide nothing.
|
||||||
assert_eq!(decisions(&state).await.len(), 8);
|
let got = decisions(&state).await;
|
||||||
|
assert!(got.iter().all(|(_, _, o, _)| o == "present"), "{got:?}");
|
||||||
|
let units: Vec<&str> = got.iter().map(|(_, u, _, _)| u.as_str()).collect();
|
||||||
|
assert_eq!(units, ["N1", "O1", "N1", "O1"]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -422,6 +422,9 @@ Les raisons longues sont dans `docs/adr/`.
|
|||||||
- ~~Texte « Paramètres par défaut » du seed (F1.2)~~ — réécrit avec N1, N1.1 et O1 selon les règles
|
- ~~Texte « Paramètres par défaut » du seed (F1.2)~~ — réécrit avec N1, N1.1 et O1 selon les règles
|
||||||
appliquées (Yvv, 2026-11-02) ; un amendement de seed gardé remplace ces propositions ouvertes à
|
appliquées (Yvv, 2026-11-02) ; un amendement de seed gardé remplace ces propositions ouvertes à
|
||||||
0 vote, et Yvv relit les textes en ligne.
|
0 vote, et Yvv relit les textes en ligne.
|
||||||
|
- **Textes du quorum** (F1.1, F1.2, N1.1 ; lot T, 2026-11-03) : réécrits pour le quorum par cran, à
|
||||||
|
relire dans `textes-quorum-seed.md` ; un second amendement de seed gardé les remplace là où personne
|
||||||
|
n'y a touché (pour F1.2 et N1.1, la garde admet la révision 2 écrite par M5, et rien d'autre).
|
||||||
- Anglais : l'interface est en anglais américain, le seed en anglais britannique.
|
- Anglais : l'interface est en anglais américain, le seed en anglais britannique.
|
||||||
|
|
||||||
## Ce qui reste après la livraison
|
## Ce qui reste après la livraison
|
||||||
|
|||||||
@@ -1,162 +0,0 @@
|
|||||||
# Brouillon — acte M6 (Propositions seed, quorum, tampon des votes, lecteur, accueil, textes du quorum)
|
|
||||||
|
|
||||||
> **Brouillon, pas figé.** Tenu par l'intégrateur libredecision-d4. Il passe sous `actes/` quand il est
|
|
||||||
> figé sur un sha, avec sa relecture indépendante inscrite. Orch (dev-63) l'exécute sous son mandat
|
|
||||||
> (« déploie » et « pousse », du 03/11 au 08/11).
|
|
||||||
|
|
||||||
## Intention
|
|
||||||
|
|
||||||
Mettre en ligne la revue d'Yvv du 02/11 et ses décisions du 02/11 et du 03/11 :
|
|
||||||
- **H** : l'accueil sous la voûte (fusionné, `a3c0a76`) ;
|
|
||||||
- **S** : les Propositions seed, la publication suspendue sans texte en vigueur, le quorum par cran, la
|
|
||||||
licence `AGPL-3.0-or-later` (fusionné, `349c8ed`) ;
|
|
||||||
- **V** : le tampon des votes, le vote simulé partout tant que la chaîne est fermée (à fusionner) ;
|
|
||||||
- **T** : les articles du seed qui décrivent le quorum (à faire) ;
|
|
||||||
- **U** : le lecteur, la ligne « N oui pour passer · N non bloquent », le réglage du quorum (à faire).
|
|
||||||
|
|
||||||
## Décisions d'Yvv que l'acte applique (registre)
|
|
||||||
|
|
||||||
- Propositions seed au cran 2 ; aucun article en vigueur au départ (02/11).
|
|
||||||
- Quorum par cran 12 · 12 · 50 · 50 · 50 dans Monnaie libre, 1 à l'essai ; l'auteur le change avant le
|
|
||||||
premier vote (02/11) ; sous le quorum, les votants manquants comptent comme « non » (03/11).
|
|
||||||
- Un vote simulé compte, puis s'efface à l'ouverture de la chaîne par un acte « supprime » (02/11).
|
|
||||||
- **Q11** (03/11, « Pas avant des votes prouvés ») : un texte entré en vigueur par des votes simulés est
|
|
||||||
en vigueur sur le site, mais ne se publie pas ; la publication attend un texte passé par les seuls
|
|
||||||
votes prouvés. Dans Monnaie libre, rien n'est publié avant l'ouverture de la chaîne.
|
|
||||||
- **Q12** (03/11, « Oui, retire-les ») : retirer les deux propositions d'inertie d'Yvv du 29/10, I1 → 5 et
|
|
||||||
E1 → 5, **avant** que le nouveau serveur démarre (étape 0 ci-dessous).
|
|
||||||
|
|
||||||
## Étape 0 — avant la poussée : retirer I1 → 5 et E1 → 5 sur la production actuelle
|
|
||||||
|
|
||||||
**Pourquoi avant** : l'amendement des Propositions seed (`seed_proposal_amend.rs`) tourne au démarrage
|
|
||||||
et n'est décidé qu'une fois par document (l. 319-334, une ligne dans `seed_proposal_amendments`). Si
|
|
||||||
une proposition d'inertie d'un membre est encore ouverte, il refuse le cran (`inertia_proposal`) pour
|
|
||||||
de bon : I1 resterait au cran 3 et E1 au cran 4. Une proposition retirée sans vote ne compte plus (sa
|
|
||||||
requête ne retient que `status IN ('open','applied')` ou une proposition votée).
|
|
||||||
|
|
||||||
**La voie : le geste d'Yvv à l'écran**, sur la version en ligne `3f10a0e`. Vérifiée le 03/11 entre
|
|
||||||
05 h 50 et 05 h 56 sur un serveur local de `3f10a0e` (base neuve du seed, proposition I1 → 5 créée
|
|
||||||
comme en production ; captures dans le scratchpad de libredecision-d4, `shots/3-row.png`,
|
|
||||||
`6-withdraw-visible.png`, `7-after.png`). Pas d'écriture SQL en production.
|
|
||||||
|
|
||||||
1. Yvv, connecté, ouvre `/g/monnaie-libre/docs/acte-engagement-certification`.
|
|
||||||
2. Il descend au bloc « Propositions en cours » et clique le filtre « Les miennes » : il ne reste
|
|
||||||
que ses deux propositions d'inertie.
|
|
||||||
3. Sous l'intertitre « INTRODUCTION » (I1) : la ligne « inertie vers 5 / 5 · très haute »,
|
|
||||||
« 0 pour · 0 contre », pastille « ta proposition ». Il clique la ligne.
|
|
||||||
4. Le vote se déplie sous la ligne (l'adresse prend `?vote=<id>`). Il clique « Coquilles,
|
|
||||||
traductions, objections, historique ».
|
|
||||||
5. Le panneau de droite s'ouvre sur « Détails » : « inertie vers 5 / 5 · très haute · proposé par
|
|
||||||
Yvv ». Il clique « Retirer ma proposition ».
|
|
||||||
6. Message « Proposition retirée. » ; la ligne disparaît des propositions en cours (en base :
|
|
||||||
`status = 'withdrawn'`, `left_at` posé ; la route relance un balayage, sans effet à 0 vote).
|
|
||||||
7. Même chose pour E1, sous l'intertitre « ENGAGEMENTS FONDAMENTAUX » (déduit de la liste groupée
|
|
||||||
par section ; non vu en local).
|
|
||||||
|
|
||||||
**Vérification**, sur une copie fraîche de la base de production :
|
|
||||||
`SELECT unit_id, status FROM proposals WHERE kind = 'inertia'` → 2 `withdrawn`, 0 `open`.
|
|
||||||
|
|
||||||
**Répétition** : sur une copie fraîche, le retrait par la route avec le serveur `3f10a0e`, puis le
|
|
||||||
démarrage du serveur M6 sur cette copie → I1, E1, E2 au cran 2, et `seed_proposal_amendments` dit
|
|
||||||
`level_outcome = 'applied'` pour les trois.
|
|
||||||
|
|
||||||
Si le retrait n'est pas fait, l'acte reste valable : I1 et E1 démarrent aux crans 3 et 4, E2 au cran 2,
|
|
||||||
et le journal dit `inertia_proposal`.
|
|
||||||
|
|
||||||
## Ce que l'acte écrit dans la base de production (donnée runtime)
|
|
||||||
|
|
||||||
- **Migrations additives** : `0009` (Propositions seed : `proposals.seed_featured`, la table
|
|
||||||
`seed_proposal_amendments`), `0010` (tampon : `ballots.mode` défaut `'chain'`,
|
|
||||||
`ballot_lines.outcome` vide, l'index `ballots_doc` ; aucune ligne réécrite), `0011` (quorums :
|
|
||||||
`documents.quorums`, `units.quorum`, la table `quorum_amendments`).
|
|
||||||
- **Amendement Propositions seed** (`2026-11-02-i1/e1/e2-proposition-seed`), par document et par
|
|
||||||
article, seulement si personne n'y a touché (0 vote, 0 bulletin, aucune coquille, révision, traduction
|
|
||||||
de membre ni signalement, origine seed, texte identique) : la version passe d'en vigueur à ouverte et
|
|
||||||
marquée ; `units.in_force_id` à NULL ; une ligne `revisions` de type `seed_proposal` ; le cran à 2,
|
|
||||||
avec une ligne `inertia_changes`, si personne n'a touché au cran (aucune proposition d'inertie
|
|
||||||
ouverte, appliquée ou votée : d'où l'étape 0) ; une ligne `seed_proposal_amendments` ; dans Monnaie
|
|
||||||
libre, une ligne `outbox`, qui attend.
|
|
||||||
- **Amendement quorums** (`2026-11-03-quorums-par-cran`), une fois par document : `documents.quorums`
|
|
||||||
= `[12,12,50,50,50]` (Monnaie libre), `[1,1,1,1,1]` (essai), là où il est NULL ; une ligne
|
|
||||||
`quorum_amendments` ; une ligne `outbox` dans Monnaie libre.
|
|
||||||
- **Le tampon** : la première mise en route n'écrit rien du tampon.
|
|
||||||
- **La publication** : suspendue. Aucun article n'est plus en vigueur dans Monnaie libre, puis (règle
|
|
||||||
« pas avant des votes prouvés », stricte, confirmée par orch le 03/11 à 06 h 11) rien ne part tant
|
|
||||||
qu'une entrée simulée ne tient pas sur les seuls votes prouvés. Le dépôt des textes garde son dernier
|
|
||||||
état.
|
|
||||||
|
|
||||||
## Effet attendu
|
|
||||||
|
|
||||||
- Les deux groupes : I1, E1 et E2 sont des « Propositions seed », ouvertes, au cran 2 ; aucun article
|
|
||||||
n'est en vigueur ; les modules (titre du document au cran 4, ordre de chaque section au cran 2) restent
|
|
||||||
en vigueur.
|
|
||||||
- Le seuil se lit en quorum : 12 · 12 · 50 · 50 · 50 votants dans Monnaie libre ; au cran 2, 9 « pour »
|
|
||||||
passent avec 3 « contre » au plus.
|
|
||||||
- **Le vote simulé s'ouvre dans Monnaie libre dès la mise en ligne** (pas de `LD_VOTE_ACCOUNT`) : toute
|
|
||||||
personne qui ouvre le groupe y vote, une personne invitée comprise ; le vote compte aussitôt, marqué
|
|
||||||
« simulé » ; 9 « pour » simulés font entrer I1, E1 ou E2 **sur le site**, sans publication. À
|
|
||||||
l'ouverture de la chaîne, l'acte « supprime » (`ld-server discard-simulation --apply`) écartera ces
|
|
||||||
votes et réexaminera ce qu'ils ont fait entrer. Effet de « simule le vote à tout endroit » et « il
|
|
||||||
compte, puis s'efface » ; orch le porte à son rapport, pas en question (03/11, 06 h 11).
|
|
||||||
- L'accueil sous la voûte.
|
|
||||||
|
|
||||||
Irréversibilité : **partielle**. Le code se remet en arrière ; les données changées restent (I1, E1, E2
|
|
||||||
sortis de la vigueur, crans à 2, quorums posés, votes simulés éventuels).
|
|
||||||
|
|
||||||
## Commande exacte
|
|
||||||
|
|
||||||
Selon la règle du canon du 02/11, un dossier « déploie » ne liste que la poussée qui met en ligne :
|
|
||||||
vers le remote `prod` (`gitea@git.open.us.org:EHV/decision.git`), branche `main`, le sha figé
|
|
||||||
(Woodpecker : `.woodpecker/app.yml`, puis `maquette.yml`). La ligne exacte est écrite au gel, dans le
|
|
||||||
dossier sous `actes/`.
|
|
||||||
|
|
||||||
## Vérifications après
|
|
||||||
|
|
||||||
- [ ] Santé vue de l'extérieur : `curl -s https://decision.librodrome.org/api/health` →
|
|
||||||
`"build":"<SHA8>"`.
|
|
||||||
- [ ] Journaux : `ssh sonic 'docker logs lpk-libredecision-app 2>&1 | grep -E "amendment|quorum|publication|ERROR|WARN"'`
|
|
||||||
→ les amendements « applied » (Propositions seed, quorums), « publication suspended », aucune
|
|
||||||
erreur.
|
|
||||||
- [ ] Redémarrage à froid (`docker compose restart` dans `/opt/libredecision-app`), santé, journaux :
|
|
||||||
aucun amendement rejoué.
|
|
||||||
- [ ] Sur une copie fraîche : les comptes de la répétition, à la ligne près ; `integrity_check` ok,
|
|
||||||
`foreign_key_check` vide ; copie retirée ensuite.
|
|
||||||
- [ ] Le dépôt des textes n'a reçu aucun commit (`git ls-remote` de
|
|
||||||
`yvv/decision-depot-textes-reference`, branche `main` : inchangée depuis avant l'acte).
|
|
||||||
- [ ] À l'écran (Yvv, à son retour) : l'accueil sous la voûte ; l'Acte d'engagement : les Propositions
|
|
||||||
seed, aucun article en vigueur ; voter dans Monnaie libre affiche « simulé ».
|
|
||||||
|
|
||||||
## Retour
|
|
||||||
|
|
||||||
- **Le code** : `docker tag libredecision-app:previous libredecision-app:latest`, puis
|
|
||||||
`cd /opt/libredecision-app && docker compose up -d --force-recreate`. `previous` sera `3f10a0e`. Le
|
|
||||||
serveur tolère les migrations qu'il ne connaît pas (`set_ignore_missing(true)`, `lib.rs:100`).
|
|
||||||
- **Les données** : pas de retour automatique. `3f10a0e` montrerait I1, E1, E2 comme propositions
|
|
||||||
ouvertes sans la marque, et aucun article en vigueur ; les votes simulés éventuels y compteraient
|
|
||||||
comme des votes du serveur. Un retour du code au-delà de quelques minutes demande un acte de données
|
|
||||||
à part.
|
|
||||||
- **La publication** : rien n'est parti, rien à retirer.
|
|
||||||
|
|
||||||
## Copie de la base de production (prise par orch, lecture seule, aucune écriture côté production)
|
|
||||||
|
|
||||||
Dans `/tmp/ld-m6-copie` (700 / 600), par `docker exec lpk-libredecision-app cat /data/<f>` pour
|
|
||||||
`libredecision.db`, `-wal`, `-shm`, puis `PRAGMA integrity_check; PRAGMA foreign_key_check;` en
|
|
||||||
`sqlite3 -readonly`. Pas un instantané atomique : `integrity_check` le dirait, et l'on reprend la
|
|
||||||
copie. Première copie : 03/11 à 06 h 11 min 12, production `3f10a0e8`, « ok », aucune clé étrangère
|
|
||||||
cassée. Seconde copie juste avant la poussée.
|
|
||||||
|
|
||||||
Lecture des prémisses : `m6/counts.sh` (scratchpad de l'intégrateur), en lecture seule. Répétition :
|
|
||||||
`m6/repetition.sh`, sur une copie de la copie, publication vers un dépôt nu local ; `/tmp/ld-m6-copie`
|
|
||||||
n'est jamais écrit.
|
|
||||||
|
|
||||||
## Prémisses à vérifier (à compléter au gel)
|
|
||||||
|
|
||||||
1. La production sert `3f10a0e` : santé `"build":"3f10a0e8"` ; `prod/main` → `3f10a0e`.
|
|
||||||
2. Sur une copie fraîche : aucune proposition d'inertie `open` ; 0 vote ; I1, E1, E2 en vigueur, aux
|
|
||||||
crans 3, 4, 4, dans les deux groupes, intouchés (sinon l'amendement les refuse avec sa raison, et
|
|
||||||
l'acte reste valable).
|
|
||||||
3. Sur la même copie : aucune proposition qui aurait déjà ses voix au nouveau seuil (attendu : 0 vote).
|
|
||||||
4. La relecture indépendante du sha figé, inscrite « ok » dans `~/harness/etat/relectures.json` ;
|
|
||||||
aucune autre relecture de ce code en cours.
|
|
||||||
5. `git diff 3f10a0e <SHA> -- docker/ .woodpecker/ .env data/` : à lire.
|
|
||||||
6. Boucle verte sur `<SHA>` et image de production construite.
|
|
||||||
@@ -65,9 +65,9 @@ Lots en cours, en copies de travail (`.claude/worktrees/agent-<id>`) :
|
|||||||
[--apply]`, jamais appelé automatiquement) ; la publication « pas avant des votes prouvés »
|
[--apply]`, jamais appelé automatiquement) ; la publication « pas avant des votes prouvés »
|
||||||
(`publish::waits_for_proven_votes`, bascule `LD_PUBLISH_SIMULATED`, règle stricte) ; `voted_units`
|
(`publish::waits_for_proven_votes`, bascule `LD_PUBLISH_SIMULATED`, règle stricte) ; `voted_units`
|
||||||
étendu ; ADR 0012 réécrite. Fini le 03/11 vers 06 h 05 (`98b4530`) : 286 tests Rust, 56 vitest, e2e
|
étendu ; ADR 0012 réécrite. Fini le 03/11 vers 06 h 05 (`98b4530`) : 286 tests Rust, 56 vitest, e2e
|
||||||
36 / 36 (C34). **Relancé à 06 h 09** pour la marque « simulé » sous l'article (VoteFold, minimal),
|
36 / 36 (C34). Puis la marque « simulé » sous l'article (VoteFold, minimal, `d57326d`), puisque M6
|
||||||
puisque M6 part sans U.
|
part sans U. **Fusionné le 03/11 à 06 h 18** (`94ddb17`).
|
||||||
- **T** (à ouvrir après V ; hors M6) : réécrire les articles du seed qui décrivent encore l'ancien plancher C
|
- **T** (`lot-t-textes`, `lot/t-textes`, parti de `39d5cd2` ; hors M6 ; **lancé le 03/11 à 06 h 46**) : réécrire les articles du seed qui décrivent encore l'ancien plancher C
|
||||||
(F1.1, F1.2, N1.1 au moins) selon le quorum par cran (ADR 0013), en fr, en, es ; amendement
|
(F1.1, F1.2, N1.1 au moins) selon le quorum par cran (ADR 0013), en fr, en, es ; amendement
|
||||||
gardé pour la production (sur le modèle de M5) ; Yvv relit les textes avant M6. Demandé par orch
|
gardé pour la production (sur le modèle de M5) ; Yvv relit les textes avant M6. Demandé par orch
|
||||||
le 03/11, reçu avant 05 h 38 (bilan des consignes).
|
le 03/11, reçu avant 05 h 38 (bilan des consignes).
|
||||||
@@ -77,9 +77,11 @@ Lots en cours, en copies de travail (`.claude/worktrees/agent-<id>`) :
|
|||||||
le premier vote** (le serveur les fournit depuis S, aucun écran ne les montre encore). Note `UX-lecteur.md` commitée (`d4531da`), 4 fichiers
|
le premier vote** (le serveur les fournit depuis S, aucun écran ne les montre encore). Note `UX-lecteur.md` commitée (`d4531da`), 4 fichiers
|
||||||
commencés. En attente de V.
|
commencés. En attente de V.
|
||||||
|
|
||||||
**M6 = H, S et V** (orch, 03/11 à 06 h 07 : Yvv a retiré lui-même I1 → 5 et E1 → 5, « tu peux livrer
|
**M6 en ligne** depuis le 03/11 à 07 h 03 (`94ddb17`, pipeline 37) : H, S et V. Production `94ddb175` ;
|
||||||
Decision ») ; T si fusionné avant, U en M7. Puis : fusion de V, boucle sur le chantier, image,
|
`main` et `chantier/acte-certification` poussés vers origin par orch. Le dépôt des textes reste à `9e0f45a`
|
||||||
répétition sur une copie de la production, dossier figé sous `actes/`, relecture indépendante, PRÊT M6.
|
(publication suspendue). Copies de répétition retirées par l'intégrateur le 03/11 à 07 h 06 (scratchpad
|
||||||
|
`m6/rep`, `m6/retour`, la base locale de `3f10a0e`) ; orch a retiré ses trois copies de la base.
|
||||||
|
Suite : **T** (en cours), puis **U** (M7), un sous-agent à la fois ; PRÊT et actes par orch.
|
||||||
|
|
||||||
Yvv relit les quatre textes en ligne. Au prochain tour, avec ses retours :
|
Yvv relit les quatre textes en ligne. Au prochain tour, avec ses retours :
|
||||||
- N1.1 : G se calcule sur p̃ rapporté à W ;
|
- N1.1 : G se calcule sur p̃ rapporté à W ;
|
||||||
@@ -148,7 +150,7 @@ chaîne (question stratégique, `docs/recherche/vote-g1-chaine.md`) ; l'affichag
|
|||||||
| Q9 | Sous le quorum, les votants qui manquent comptent-ils comme des « non » ? (Sinon, un « non » peut faire passer un texte en complétant le quorum.) | **tranché** (03/11, formulaire d'orch racine vers 03 h 45) : « Ils comptent comme non » ; au cran 2, quorum 12, 9 oui passent avec 3 non au plus |
|
| Q9 | Sous le quorum, les votants qui manquent comptent-ils comme des « non » ? (Sinon, un « non » peut faire passer un texte en complétant le quorum.) | **tranché** (03/11, formulaire d'orch racine vers 03 h 45) : « Ils comptent comme non » ; au cran 2, quorum 12, 9 oui passent avec 3 non au plus |
|
||||||
| Q10 | Licence : AGPL-3.0 seulement, ou « ou toute version ultérieure » ? | **tranché** (03/11, même formulaire) : « Ou version ultérieure » → `AGPL-3.0-or-later` dans les manifestes (`Cargo.toml`, `web/package.json` et son verrou) et le README ; `LICENSE` inchangé ; confié au lot S |
|
| Q10 | Licence : AGPL-3.0 seulement, ou « ou toute version ultérieure » ? | **tranché** (03/11, même formulaire) : « Ou version ultérieure » → `AGPL-3.0-or-later` dans les manifestes (`Cargo.toml`, `web/package.json` et son verrou) et le README ; `LICENSE` inchangé ; confié au lot S |
|
||||||
| Q11 | Un texte entré en vigueur par des votes simulés (9 « pour » au cran 2 dans Monnaie libre) se publie-t-il dans le dépôt des textes ? | **tranché** (03/11, formulaire d'orch, reçu avant 05 h 35 ; le relais disait « vers 05 h 45 ») : « Pas avant des votes prouvés » → en vigueur sur le site, pas publié ; la publication attend un texte passé par les seuls votes prouvés (dans Monnaie libre, rien avant l'ouverture de la chaîne) ; codé par V, écrit au dossier M6 |
|
| Q11 | Un texte entré en vigueur par des votes simulés (9 « pour » au cran 2 dans Monnaie libre) se publie-t-il dans le dépôt des textes ? | **tranché** (03/11, formulaire d'orch, reçu avant 05 h 35 ; le relais disait « vers 05 h 45 ») : « Pas avant des votes prouvés » → en vigueur sur le site, pas publié ; la publication attend un texte passé par les seuls votes prouvés (dans Monnaie libre, rien avant l'ouverture de la chaîne) ; codé par V, écrit au dossier M6 |
|
||||||
| Q12 | En production, Yvv a ouvert le 29/10 deux propositions d'inertie, I1 → 5 et E1 → 5 (0 vote). Ouvertes au démarrage de M6, elles retiennent I1 et E1 aux crans 3 et 4 pour de bon (amendement décidé une fois). Les retirer ? | **tranché** (03/11, formulaire d'orch, reçu à 05 h 51) : « Oui, retire-les » → avant le démarrage de M6, par le geste d'Yvv à l'écran (« Les miennes » › la ligne d'inertie › « Coquilles, traductions, objections, historique » › « Retirer ma proposition »), vérifié sur `3f10a0e` en local ; étape 0 du brouillon M6 (`M6-brouillon.md`) |
|
| Q12 | En production, Yvv a ouvert le 29/10 deux propositions d'inertie, I1 → 5 et E1 → 5 (0 vote). Ouvertes au démarrage de M6, elles retiennent I1 et E1 aux crans 3 et 4 pour de bon (amendement décidé une fois). Les retirer ? | **tranché** (03/11, formulaire d'orch, reçu à 05 h 51) : « Oui, retire-les » → avant le démarrage de M6, par le geste d'Yvv à l'écran (« Les miennes » › la ligne d'inertie › « Coquilles, traductions, objections, historique » › « Retirer ma proposition »), vérifié sur `3f10a0e` en local ; fait par Yvv à l'écran (annoncé à 06 h 07), dossier `actes/2026-11-03-m6-propositions-seed-quorum-tampon.md` |
|
||||||
|
|
||||||
Défauts posés par orch, sans question (Yvv peut les corriger) :
|
Défauts posés par orch, sans question (Yvv peut les corriger) :
|
||||||
- Décisions : « modules restant à voter » = les modules du doc pas encore adoptés par un vote
|
- Décisions : « modules restant à voter » = les modules du doc pas encore adoptés par un vote
|
||||||
@@ -168,9 +170,27 @@ Défauts posés par orch, sans question (Yvv peut les corriger) :
|
|||||||
- M2 + M3a (L9 et L10) : **caduc, emporté par M4** (`actes/2026-10-31-m2-vote.md`) : jamais exécuté, `74f96be` est en production depuis M4.
|
- M2 + M3a (L9 et L10) : **caduc, emporté par M4** (`actes/2026-10-31-m2-vote.md`) : jamais exécuté, `74f96be` est en production depuis M4.
|
||||||
- M4 (L9 à L13) : **exécuté** le 01/11/2026, 22 h 52 – 23 h 40 (`actes/2026-10-31-m4-vote-modules-objections.md`) ; M2 et M3a n'ont plus d'objet.
|
- M4 (L9 à L13) : **exécuté** le 01/11/2026, 22 h 52 – 23 h 40 (`actes/2026-10-31-m4-vote-modules-objections.md`) ; M2 et M3a n'ont plus d'objet.
|
||||||
- M5 (lot G, articles des règles, licence) : **exécuté** le 02/11/2026, 02 h 53 – 03 h 10 (`actes/2026-11-02-m5-retenue-textes-regles.md`).
|
- M5 (lot G, articles des règles, licence) : **exécuté** le 02/11/2026, 02 h 53 – 03 h 10 (`actes/2026-11-02-m5-retenue-textes-regles.md`).
|
||||||
|
- M6 (H, S, V) : **exécuté** le 03/11/2026 par orch sous mandat, en ligne à 07 h 03 (pipeline 37) ; figé le 03/11 à 06 h 22 sur `94ddb17` (`actes/2026-11-03-m6-propositions-seed-quorum-tampon.md`) ; répétition faite sur une copie de la production de 06 h 11 ; relecture indépendante « 94ddb17 : ok » inscrite à 06 h 42 ; retour corrigé (couper `LD_PUBLISH_REMOTE` avant, répété) ; PRÊT envoyé à orch (dev-63), qui exécute sous mandat.
|
||||||
|
|
||||||
Restes mineurs de `71819f9` et du lot B : faits dans le lot D.
|
Restes mineurs de `71819f9` et du lot B : faits dans le lot D.
|
||||||
|
|
||||||
|
Liste d'attente (relecture de `94ddb17`, M6) :
|
||||||
|
- `sbom-publish` échoue encore (pipelines 36 et 37) : écart ancien (certificat du serveur
|
||||||
|
Dependency-Track refusé, HANDOFF), étape en `failure: ignore` ;
|
||||||
|
- **acte « supprime »** (ouverture de la chaîne) : le jouer serveur arrêté, ou ajouter des gardes
|
||||||
|
`WHERE in_force_id = …` à `give_back` (`simulation.rs:200`, `:258-298`) ; `give_back` remet la version
|
||||||
|
déplacée telle qu'elle est (révisée entre-temps, une révision non votée reviendrait) ; `confirm_seeds`
|
||||||
|
(`adoption.rs:49`) : un module confirmé par des votes simulés le reste ; p̃ mesurée votes simulés
|
||||||
|
compris jusqu'au lendemain de l'acte ;
|
||||||
|
- **lien d'équipe** : il multiplie les voix simulées (jusqu'à 500 ouvertures) ; l'ADR 0012 l. 290 dit
|
||||||
|
« deux voix » : à corriger ; à dire à Yvv ;
|
||||||
|
- **U** : « Vote enregistré. » (`VoteFold.svelte:123`) et « N votes enregistrés. » (`BallotPanel.svelte:95`)
|
||||||
|
après un vote simulé ; `fr.ts:263`, `:348` (et en, es) : « jusqu'à son effacement à l'ouverture de la
|
||||||
|
chaîne » → c'est l'acte « supprime » qui efface ;
|
||||||
|
- `document.json` comptera les votes simulés une fois la publication permise (l'ADR le dit) ;
|
||||||
|
`simulation::unproven` recharge l'état du document à chaque affichage tant que la file attend
|
||||||
|
(performance) ; `reached` / `missing` = 0 alors que la marge retient le rival (antérieur).
|
||||||
|
|
||||||
Liste d'attente (relecture de `033b331+4d4adcc`, après M4) :
|
Liste d'attente (relecture de `033b331+4d4adcc`, après M4) :
|
||||||
- 0008 : lever les doublons debout avant de créer `signals_one_standing` (sans objet en production : 0 signalement) ;
|
- 0008 : lever les doublons debout avant de créer `signals_one_standing` (sans objet en production : 0 signalement) ;
|
||||||
- un ordre ne départage pas deux articles à la même position (deux créations simultanées) ;
|
- un ordre ne départage pas deux articles à la même position (deux créations simultanées) ;
|
||||||
|
|||||||
@@ -0,0 +1,402 @@
|
|||||||
|
# Textes du seed : le quorum par cran (F1.1, F1.2, N1.1)
|
||||||
|
|
||||||
|
> Préparé le 2026-11-03 pour ta relecture, Yvv (lot T), d'après tes décisions : le quorum (02/11 :
|
||||||
|
> « C suit le cran, mais c'est une proposition par défaut, variable modifiable par l'auteur initial
|
||||||
|
> du texte […] c'est plutôt un quorum ») ; « La courbe d'aujourd'hui, après le quorum » ; sous le
|
||||||
|
> quorum, « Ils comptent comme non » (03/11) ; les quorums 12 · 12 · 50 · 50 · 50 dans Monnaie libre ;
|
||||||
|
> « L'auteur, avant le 1er vote » ; les Propositions seed au cran 2 (ADR 0011). Les nouveaux textes sont
|
||||||
|
> collés dans `seeds/acte-engagement-certification.json` et dans un amendement gardé, sur la branche
|
||||||
|
> `lot/t-textes` : **rien n'est en ligne**.
|
||||||
|
|
||||||
|
## Avant de lire
|
||||||
|
|
||||||
|
- **Ce qui change.** Trois articles décrivaient encore l'ancien plancher C de voix « pour » :
|
||||||
|
« Formule du seuil WoT » (F1.1), « Paramètres par défaut » (F1.2), « Niveaux d'inertie » (N1.1).
|
||||||
|
Ils disent désormais le quorum, dans cet ordre : d'abord le sens (sous le quorum, les votants qui
|
||||||
|
manquent comptent comme « non » ; au-delà, la part de « pour » du cran), puis la formule en code,
|
||||||
|
telle que le moteur la calcule (`crates/ld-engine/src/threshold.rs:3-6`). F1.2 et N1.1 disent aussi
|
||||||
|
le départ d'après l'ADR 0011 : aucun article en vigueur, Raison d'être, Unicité du compte et
|
||||||
|
Réciprocité en Propositions seed au cran 2, le titre du document au cran 4.
|
||||||
|
- **Les titres** sont gardés tous les trois : ils restent vrais.
|
||||||
|
- **Les nombres.** Chaque nombre de ces textes, dans les trois langues, est calculé par le moteur ou
|
||||||
|
lu dans le seed par un test (`crates/ld-server/tests/seed_quorum_texts.rs`,
|
||||||
|
`every_number_of_the_quorum_texts_is_the_engine_s`) : un chiffre que rien ne justifie fait échouer
|
||||||
|
le test. Les trois formules écrites dans les textes sont évaluées telles quelles et comparées au
|
||||||
|
moteur sur une grille de cas (`the_formulas_of_the_texts_are_those_the_engine_computes`).
|
||||||
|
- **La forme**, vérifiée par le test `the_quorum_texts_follow_the_rules_of_form` et par `render()`
|
||||||
|
(`web/src/lib/markdown.ts`) : sous-ensemble de Markdown, ni tableau ni intertitre ; une ligne vide
|
||||||
|
avant chaque liste (toutes les listes sortent en `<ul>`) ; renvois par le titre (« Formule du seuil
|
||||||
|
WoT », « Paramètres par défaut », « Niveaux d'inertie »), jamais par le code ; apostrophes droites ;
|
||||||
|
« » espacés en français, “ ” en anglais britannique (*favour*), «» serrés en espagnol ; formules en
|
||||||
|
code, avec × et −, jamais d'astérisque.
|
||||||
|
- **Longueur** (signes) : fr : F1.1 1081 · F1.2 2055 · N1.1 2372 ; en : F1.1 1058 · F1.2 1977 · N1.1 2246 ; es : F1.1 1083 · F1.2 2035 · N1.1 2454. Limite d'une proposition : 4 000 (`crates/ld-server/src/proposals.rs:32`) ;
|
||||||
|
au-delà de 700, l'assistant dit « texte long » (`crates/ld-engine/src/assist.rs:25`), comme pour les
|
||||||
|
textes actuels de F1.2 et N1.1.
|
||||||
|
- **Le ton** est celui de F1.2, N1 et O1 tels que M5 les a posés : le groupe, la personne, jamais le
|
||||||
|
système. Rien sur le vote simulé, ni sur le recoupement entre la chaîne et les totaux.
|
||||||
|
|
||||||
|
## La production
|
||||||
|
|
||||||
|
Les trois articles y sont des propositions ouvertes du seed, à 0 vote. Un **amendement de seed
|
||||||
|
gardé** (`crates/ld-server/src/seed_amend.rs`, `2026-11-03-f1-1-quorum`, `-f1-2-quorum`,
|
||||||
|
`-n1-1-quorum`) remplace leur texte au démarrage, une seule fois par document, **seulement si
|
||||||
|
personne n'y a touché** : origine du seed, jamais entré en vigueur ni confirmé ; aucun vote ; aucune
|
||||||
|
ligne de bulletin, de la chaîne ou de la simulation, en attente ou prouvée, quel qu'en soit le sort ;
|
||||||
|
aucune coquille ; aucune traduction écrite par un membre ; aucune objection ; le texte d'avant
|
||||||
|
identique à l'empreinte attendue.
|
||||||
|
|
||||||
|
- **F1.1** est encore à sa révision 1 (le texte du 21/10) : la garde de toujours.
|
||||||
|
- **F1.2 et N1.1** sont à la révision 2 depuis M5 (02/11). La garde est étendue pour eux, et pour eux
|
||||||
|
seuls : elle admet la révision 2 dont l'historique est « texte initial » puis « texte de départ
|
||||||
|
remplacé », et sur laquelle l'amendement de M5 de cet article, lui seul, a été appliqué
|
||||||
|
(`seed_amendments`) ; le texte doit être celui que M5 a posé. Rien d'autre n'est admis.
|
||||||
|
- Les amendements de M5 sur F1.2 et N1.1 sont **dépassés** : le seed ne donne plus leur texte, ils ne
|
||||||
|
décident plus rien (sans erreur au journal) ; leurs décisions du 02/11 restent.
|
||||||
|
|
||||||
|
Appliqué, dans une transaction :
|
||||||
|
- `proposals` : le titre et le texte (fr) remplacés, la révision + 1 (F1.1 : 2 ; F1.2 et N1.1 : 3) ;
|
||||||
|
- `revisions` : une ligne « texte de départ remplacé » (`amendment`), sans auteur, avec le nouveau
|
||||||
|
texte : l'historique de l'article garde l'ancien et montre le diff ;
|
||||||
|
- `translations` : en et es remplacés, liés à la nouvelle empreinte ;
|
||||||
|
- `seed_amendments` : la décision (`applied`, `refused` et sa raison, ou `present`), aussi au journal ;
|
||||||
|
- `outbox` : dans Monnaie libre seulement, une ligne par article (« proposition de départ réécrite :
|
||||||
|
F1.1, le quorum par cran »…), qui attend comme les autres le premier texte en vigueur ; l'essai ne
|
||||||
|
publie jamais.
|
||||||
|
|
||||||
|
Rien d'autre n'est touché : ni un vote, ni un bulletin, ni une autre proposition, ni le quorum ou le
|
||||||
|
cran d'un article. Testé sur une base faite comme la production après M6 (née avec les textes du
|
||||||
|
21/10, démarrée par le seed de M5 puis par celui de M6) : appliqué une fois ; refusé là où quelqu'un a
|
||||||
|
voté (raison `votes`), là où un bulletin est en attente (`ballots`), là où un membre a écrit une
|
||||||
|
traduction (`translation_edited`) ; au redémarrage, rien n'est rejoué.
|
||||||
|
|
||||||
|
**À savoir avant la mise en ligne** : le vote simulé est ouvert dans Monnaie libre depuis M6. Si
|
||||||
|
quelqu'un vote sur l'un de ces trois articles avant que ce code parte, l'amendement le refuse là, et le
|
||||||
|
texte d'avant reste (on le proposerait alors par l'application). Le refus est consigné une fois pour
|
||||||
|
toutes : l'acte « supprime » de la fin de la simulation ne le fera pas rejouer.
|
||||||
|
|
||||||
|
## F1.1 — Formule du seuil WoT
|
||||||
|
|
||||||
|
**Aujourd'hui** (en production à la révision 1, le texte du 21/10) : en tête, en gras, `Seuil = C + B^W + (M + (1-M) × (1 - (T/W)^G)) × max(0, T-C)` ; « C = constante de base (plancher fixe) », « B = exposant de base (B^W tend vers 0) », « M = ratio de majorité cible (M = majority_pct / 100) » ; la liste des lettres suit « Où : » sans ligne vide, et s'affiche avec ses tirets ; « Seuil » reste en français dans les versions anglaise et espagnole.
|
||||||
|
|
||||||
|
**Ce qui est faux** : il n'y a plus de plancher C. Chaque cran a un quorum Q ; sous le quorum, les votants qui manquent comptent comme « non » ; au-delà, la part du cran s'applique à tous les votants. Le moteur calcule `R = B^W + (M + (1 − M)(1 − (T'/W)^G)) · T'`, avec `T' = max(T, Q)`. B est la base (W l'exposant).
|
||||||
|
|
||||||
|
**Titre** gardé : il reste vrai. Le texte dit d'abord le sens, puis la formule, puis les lettres ; leurs valeurs sont dans les deux articles voisins.
|
||||||
|
|
||||||
|
### F1.1 · Français (fait foi)
|
||||||
|
|
||||||
|
Titre : **Formule du seuil WoT**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Le seuil est le nombre de voix pour qu'il faut à une version pour entrer en vigueur. Sous le quorum Q, les votants qui manquent comptent comme « non » : le seuil se calcule comme si Q personnes avaient voté. Au-delà, il faut la part de « pour » que demande le cran d'inertie de l'article, parmi tous les votants : presque l'unanimité quand peu de gens votent, la majorité M quand tout le monde vote.
|
||||||
|
|
||||||
|
`T' = max(T, Q)`
|
||||||
|
`Seuil = B^W + (M + (1 − M) × (1 − (T'/W)^G)) × T'`
|
||||||
|
|
||||||
|
Une version passe quand ses voix pour atteignent le seuil, arrondi à l'entier au-dessus.
|
||||||
|
|
||||||
|
- **T** : les votes exprimés sur la version, pour et contre.
|
||||||
|
- **Q** : le quorum de l'article ; **T'** : les votants retenus, T, ou Q s'il en manque.
|
||||||
|
- **W** : le nombre de membres de la toile de confiance Ğ1.
|
||||||
|
- **B** : la base ; à la puissance W, elle est si petite qu'elle ne compte pas.
|
||||||
|
- **M** : la majorité demandée quand tout le monde vote.
|
||||||
|
- **G** : le gradient : plus il est grand, plus la part demandée monte quand peu de gens votent.
|
||||||
|
|
||||||
|
Leurs valeurs sont dans « Paramètres par défaut » et « Niveaux d'inertie ».
|
||||||
|
```
|
||||||
|
|
||||||
|
### F1.1 · English
|
||||||
|
|
||||||
|
Titre : **WoT threshold formula**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
The threshold is the number of votes in favour a version needs to come into force. Below the quorum Q, the voters who are missing count as votes against: the threshold is worked out as if Q people had voted. Beyond it, it takes the share of votes in favour that the article's level of inertia asks for, among all the voters: near-unanimity when few people vote, the majority M when everyone votes.
|
||||||
|
|
||||||
|
`T' = max(T, Q)`
|
||||||
|
`Threshold = B^W + (M + (1 − M) × (1 − (T'/W)^G)) × T'`
|
||||||
|
|
||||||
|
A version passes when its votes in favour reach the threshold, rounded up to a whole number.
|
||||||
|
|
||||||
|
- **T**: the votes cast on the version, for and against.
|
||||||
|
- **Q**: the article's quorum; **T'**: the voters counted, T, or Q if some are missing.
|
||||||
|
- **W**: the number of members of the Ğ1 web of trust.
|
||||||
|
- **B**: the base; raised to the power W, it is so small that it does not count.
|
||||||
|
- **M**: the majority asked for when everyone votes.
|
||||||
|
- **G**: the gradient: the larger it is, the higher the share asked for when few people vote.
|
||||||
|
|
||||||
|
Their values are in “Default parameters” and “Inertia levels”.
|
||||||
|
```
|
||||||
|
|
||||||
|
### F1.1 · Español
|
||||||
|
|
||||||
|
Titre : **Fórmula del umbral WoT**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
El umbral es el número de votos a favor que necesita una versión para entrar en vigor. Por debajo del quórum Q, los votantes que faltan cuentan como «no»: el umbral se calcula como si hubieran votado Q personas. Más allá, hace falta la proporción de votos a favor que pide el nivel de inercia del artículo, entre todos los votantes: casi la unanimidad cuando vota poca gente, la mayoría M cuando vota todo el mundo.
|
||||||
|
|
||||||
|
`T' = max(T, Q)`
|
||||||
|
`Umbral = B^W + (M + (1 − M) × (1 − (T'/W)^G)) × T'`
|
||||||
|
|
||||||
|
Una versión pasa cuando sus votos a favor alcanzan el umbral, redondeado al entero superior.
|
||||||
|
|
||||||
|
- **T**: los votos emitidos sobre la versión, a favor y en contra.
|
||||||
|
- **Q**: el quórum del artículo; **T'**: los votantes contados, T, o Q si faltan.
|
||||||
|
- **W**: el número de miembros de la red de confianza Ğ1.
|
||||||
|
- **B**: la base; elevada a la potencia W, es tan pequeña que no cuenta.
|
||||||
|
- **M**: la mayoría pedida cuando vota todo el mundo.
|
||||||
|
- **G**: el gradiente: cuanto mayor es, más alta es la proporción pedida cuando vota poca gente.
|
||||||
|
|
||||||
|
Sus valores están en «Parámetros por defecto» y «Niveles de inercia».
|
||||||
|
```
|
||||||
|
|
||||||
|
## F1.2 — Paramètres par défaut
|
||||||
|
|
||||||
|
**Aujourd'hui** (en production à la révision 2, le texte de M5 du 02/11) : « **C = 50** : le nombre minimal de votes « pour » dans Monnaie libre. Sous 50 voix pour, rien ne passe » ; « le groupe demande 75 % de « pour » au-delà de C » ; « Au départ, Unicité du compte, Réciprocité et le titre du document sont au cran 4 (haute) ».
|
||||||
|
|
||||||
|
**Ce qui est faux** : C est devenu le quorum du cran (12 · 12 · 50 · 50 · 50 dans Monnaie libre), que l'auteur de la première version change avant le premier vote ; la part du cran s'applique à tous les votants, plus « au-delà de C » ; au départ, aucun article n'est en vigueur, et Raison d'être, Unicité du compte et Réciprocité sont des Propositions seed au cran 2.
|
||||||
|
|
||||||
|
**Titre** gardé. L'introduction, « Un vote permanent, sans durée » et les autres lettres sont ceux de M5, mot pour mot : seuls la ligne de C (devenue Q) et « Par défaut » changent.
|
||||||
|
|
||||||
|
### F1.2 · Français (fait foi)
|
||||||
|
|
||||||
|
Titre : **Paramètres par défaut**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Les valeurs des lettres de la formule (voir « Formule du seuil WoT ») et la façon dont le vote se déroule.
|
||||||
|
|
||||||
|
**Un vote permanent, sans durée**
|
||||||
|
|
||||||
|
- Chaque version se vote à tout moment, sans échéance. Chacun peut changer ou retirer son vote : le dernier fait foi.
|
||||||
|
- Une proposition entre en vigueur dès qu'elle atteint son seuil. S'il y a déjà un texte en vigueur, il lui faut en plus une marge (voix pour moins voix exigées) plus large que celle de ce texte. Le texte remplacé redevient une proposition, avec ses votes : il peut revenir.
|
||||||
|
- Si plusieurs propositions y parviennent, celle qui a la marge la plus large entre en vigueur.
|
||||||
|
|
||||||
|
**Les lettres**
|
||||||
|
|
||||||
|
- **W** : le nombre de membres de la toile de confiance Ğ1, relu sur la chaîne toutes les 6 heures.
|
||||||
|
- **T** : les votes exprimés sur la version, pour et contre.
|
||||||
|
- **Q**, le quorum : sous ce nombre de votants, ceux qui manquent comptent comme « non ». Chaque article prend celui de son cran : 12, 12, 50, 50 et 50 dans Monnaie libre, du cran 1 au cran 5. L'auteur de la première version d'un article, un administrateur pour les textes de départ, peut en fixer un autre tant que personne n'a voté sur l'article ; ensuite, il ne change plus. Dans Monnaie libre, jamais moins de 2 : une seule voix ne fait jamais passer un texte.
|
||||||
|
- **B = 0,1** : dans la toile de confiance, `B^W` est si petit qu'il ne compte pas.
|
||||||
|
- **M** et **G** : donnés par le cran d'inertie de l'article (voir « Niveaux d'inertie »). G n'est pas fixé à la main : il se déduit de la participation habituelle du groupe.
|
||||||
|
|
||||||
|
**Par défaut**
|
||||||
|
|
||||||
|
- Le cran par défaut est le 3 (moyenne) : M = 55 %, un quorum de 50 dans Monnaie libre, et le groupe demande 75 % de « pour » quand il vote comme d'habitude. Tout nouvel article commence à ce cran.
|
||||||
|
- Au départ, aucun article n'est en vigueur. Raison d'être, Unicité du compte et Réciprocité sont des Propositions seed, mises en avant pour lancer le vote, au cran 2 (basse). Le titre du document est au cran 4 (haute), l'ordre des articles de chaque section au cran 2, tous les autres articles au cran 3.
|
||||||
|
```
|
||||||
|
|
||||||
|
### F1.2 · English
|
||||||
|
|
||||||
|
Titre : **Default parameters**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
The values of the letters of the formula (see “WoT threshold formula”) and how the vote runs.
|
||||||
|
|
||||||
|
**A permanent vote, with no duration**
|
||||||
|
|
||||||
|
- Every version can be voted on at any time, with no deadline. Anyone can change or withdraw their vote: the last one is the one that counts.
|
||||||
|
- A proposal comes into force as soon as it reaches its threshold. If a text is already in force, it also needs a wider margin (votes in favour minus votes required) than that text has. The replaced text becomes a proposal again, with its votes: it can come back.
|
||||||
|
- If several proposals get there, the one with the widest margin comes into force.
|
||||||
|
|
||||||
|
**The letters**
|
||||||
|
|
||||||
|
- **W**: the number of members of the Ğ1 web of trust, read again from the chain every 6 hours.
|
||||||
|
- **T**: the votes cast on the version, for and against.
|
||||||
|
- **Q**, the quorum: below this number of voters, those who are missing count as votes against. Each article takes the one of its level: 12, 12, 50, 50 and 50 in Monnaie libre, from level 1 to level 5. The author of the first version of an article, an administrator for the starting texts, can set another one as long as nobody has voted on the article; after that, it no longer changes. In Monnaie libre, never less than 2: a single vote never passes a text.
|
||||||
|
- **B = 0.1**: in the web of trust, `B^W` is so small that it does not count.
|
||||||
|
- **M** and **G**: given by the article's level of inertia (see “Inertia levels”). G is not set by hand: it follows from the group's usual turnout.
|
||||||
|
|
||||||
|
**By default**
|
||||||
|
|
||||||
|
- The default level is 3 (medium): M = 55%, a quorum of 50 in Monnaie libre, and the group asks for 75% of votes in favour when it votes as usual. Every new article starts at this level.
|
||||||
|
- At the start, no article is in force. Purpose, Uniqueness of the account and Reciprocity are seed proposals, put forward to start the vote, at level 2 (low). The title of the document is at level 4 (high), the order of the articles of each section at level 2, all other articles at level 3.
|
||||||
|
```
|
||||||
|
|
||||||
|
### F1.2 · Español
|
||||||
|
|
||||||
|
Titre : **Parámetros por defecto**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Los valores de las letras de la fórmula (ver «Fórmula del umbral WoT») y cómo se desarrolla la votación.
|
||||||
|
|
||||||
|
**Una votación permanente, sin duración**
|
||||||
|
|
||||||
|
- Cada versión se vota en cualquier momento, sin plazo. Cada cual puede cambiar o retirar su voto: el último es el que cuenta.
|
||||||
|
- Una propuesta entra en vigor en cuanto alcanza su umbral. Si ya hay un texto en vigor, necesita además un margen (votos a favor menos votos exigidos) más amplio que el de ese texto. El texto sustituido vuelve a ser una propuesta, con sus votos: puede volver.
|
||||||
|
- Si varias propuestas lo consiguen, entra en vigor la que tiene el margen más amplio.
|
||||||
|
|
||||||
|
**Las letras**
|
||||||
|
|
||||||
|
- **W**: el número de miembros de la red de confianza Ğ1, leído de nuevo en la cadena cada 6 horas.
|
||||||
|
- **T**: los votos emitidos sobre la versión, a favor y en contra.
|
||||||
|
- **Q**, el quórum: por debajo de este número de votantes, los que faltan cuentan como «no». Cada artículo toma el de su nivel: 12, 12, 50, 50 y 50 en Monnaie libre, del nivel 1 al nivel 5. El autor de la primera versión de un artículo, un administrador para los textos de partida, puede fijar otro mientras nadie haya votado sobre el artículo; después, ya no cambia. En Monnaie libre, nunca menos de 2: un solo voto nunca hace pasar un texto.
|
||||||
|
- **B = 0,1**: en la red de confianza, `B^W` es tan pequeño que no cuenta.
|
||||||
|
- **M** y **G**: los da el nivel de inercia del artículo (ver «Niveles de inercia»). G no se fija a mano: se deduce de la participación habitual del grupo.
|
||||||
|
|
||||||
|
**Por defecto**
|
||||||
|
|
||||||
|
- El nivel por defecto es el 3 (media): M = 55 %, un quórum de 50 en Monnaie libre, y el grupo pide un 75 % de votos a favor cuando vota como de costumbre. Todo artículo nuevo empieza en este nivel.
|
||||||
|
- Al inicio, ningún artículo está en vigor. Razón de ser, Unicidad de la cuenta y Reciprocidad son Propuestas seed, puestas en primer plano para lanzar la votación, en el nivel 2 (baja). El título del documento está en el nivel 4 (alta), el orden de los artículos de cada sección en el nivel 2, todos los demás artículos en el nivel 3.
|
||||||
|
```
|
||||||
|
|
||||||
|
## N1.1 — Niveaux d'inertie
|
||||||
|
|
||||||
|
**Aujourd'hui** (en production à la révision 2, le texte de M5) : « le groupe demande d'abord C voix pour, puis une part de « pour » parmi les votes au-delà de C » ; la part exigée sur T ; « Cran 4 · haute […] Unicité du compte, Réciprocité, le titre du document » ; la participation de référence « vaut deux fois C » ; l'exemple « 92, 97, 103, 109 ou 113 voix pour […] Au cran 3 : 50 voix pour, plus 75 % des 70 autres votes, soit 102,5, arrondi à 103 ».
|
||||||
|
|
||||||
|
**Ce qui est faux** : le quorum remplace C ; la part se calcule sur T', les votants ou le quorum s'il en manque ; au cran 2 sont les trois Propositions seed et l'ordre de chaque section, au cran 4 le titre seul ; la référence est deux fois le quorum de référence du document (50 dans Monnaie libre), jamais le quorum d'un article ; avec le quorum, l'exemple donne 72, 81, 90, 100 ou 108 voix pour ; et dans la formule de G, p̃ est une part de l'électorat, pas un nombre de votants.
|
||||||
|
|
||||||
|
**Titre** gardé : il reste vrai. Chaque cran dit maintenant son quorum ; l'exemple dit aussi deux cas sous le quorum.
|
||||||
|
|
||||||
|
### N1.1 · Français (fait foi)
|
||||||
|
|
||||||
|
Titre : **Niveaux d'inertie**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
À chaque cran, le groupe demande un quorum de votants, puis une part de « pour » parmi eux (voir « Formule du seuil WoT »). Sous le quorum, les votants qui manquent comptent comme « non ». La part dépend du cran et du nombre de votants, comparé à la participation habituelle du groupe : plus il y a de votants, plus elle baisse, jusqu'à la majorité M quand tout le monde vote.
|
||||||
|
|
||||||
|
`Part exigée = M + (1 − M) × (1 − (T'/W)^G)`, où T' est le nombre de votants, ou le quorum s'il en manque.
|
||||||
|
|
||||||
|
**Les cinq crans** : le quorum dans Monnaie libre, la part exigée quand le groupe vote comme d'habitude, puis quand tout le monde vote, et ce qui est à ce cran au départ.
|
||||||
|
|
||||||
|
- **Cran 1 · très basse** : quorum 12 ; 60 % comme d'habitude, 50 % quand tout le monde vote. Aucun article au départ.
|
||||||
|
- **Cran 2 · basse** : quorum 12 ; 67 % comme d'habitude, 50 % quand tout le monde vote. Raison d'être, Unicité du compte, Réciprocité, et l'ordre des articles de chaque section.
|
||||||
|
- **Cran 3 · moyenne** : quorum 50 ; 75 % comme d'habitude, 55 % quand tout le monde vote. Le cran par défaut : tous les autres articles, et chaque nouvel article.
|
||||||
|
- **Cran 4 · haute** : quorum 50 ; 83 % comme d'habitude, 60 % quand tout le monde vote. Le titre du document.
|
||||||
|
- **Cran 5 · très haute** : quorum 50 ; 90 % comme d'habitude, 66 % quand tout le monde vote. Aucun article au départ.
|
||||||
|
|
||||||
|
**La participation habituelle** est la médiane du nombre de votants des propositions du groupe votées depuis un an. Tant que le groupe n'a pas voté sur 5 propositions, elle vaut deux fois le quorum de référence du document : 100 votants dans Monnaie libre. Elle ne dépend pas du quorum d'un article. Elle reste entre 1 % et 50 % de l'électorat, et se mesure une fois par jour.
|
||||||
|
|
||||||
|
**G** se calcule pour que la part exigée, à la participation habituelle, soit exactement celle du cran : `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, où s est la part exigée comme d'habitude et p̃ la participation habituelle divisée par W.
|
||||||
|
|
||||||
|
Exemple : avec 7 224 membres et une participation habituelle de 120 votants, quand 120 personnes votent, il faut 72, 81, 90, 100 ou 108 voix pour, du cran 1 au cran 5 ; au cran 3, 75 % des 120 votants, soit 90. Sous le quorum, les absents comptent comme « non » : au cran 2 (quorum 12), 9 voix pour passent avec 3 « contre » au plus ; au cran 3 (quorum 50), il faut 39 voix pour, avec 11 « contre » au plus.
|
||||||
|
```
|
||||||
|
|
||||||
|
### N1.1 · English
|
||||||
|
|
||||||
|
Titre : **Inertia levels**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
At each level, the group asks for a quorum of voters, then for a share of votes in favour among them (see “WoT threshold formula”). Below the quorum, the voters who are missing count as votes against. The share depends on the level and on the number of voters, compared with the group's usual turnout: the more voters, the lower it gets, down to the majority M when everyone votes.
|
||||||
|
|
||||||
|
`Share asked = M + (1 − M) × (1 − (T'/W)^G)`, where T' is the number of voters, or the quorum if some are missing.
|
||||||
|
|
||||||
|
**The five levels**: the quorum in Monnaie libre, the share asked when the group votes as usual, then when everyone votes, and what is at that level at the start.
|
||||||
|
|
||||||
|
- **Level 1 · very low**: quorum 12; 60% as usual, 50% when everyone votes. No article at the start.
|
||||||
|
- **Level 2 · low**: quorum 12; 67% as usual, 50% when everyone votes. Purpose, Uniqueness of the account, Reciprocity, and the order of the articles of each section.
|
||||||
|
- **Level 3 · medium**: quorum 50; 75% as usual, 55% when everyone votes. The default level: all other articles, and every new article.
|
||||||
|
- **Level 4 · high**: quorum 50; 83% as usual, 60% when everyone votes. The title of the document.
|
||||||
|
- **Level 5 · very high**: quorum 50; 90% as usual, 66% when everyone votes. No article at the start.
|
||||||
|
|
||||||
|
**The usual turnout** is the median number of voters on the group's proposals voted on over the past year. Until the group has voted on 5 proposals, it is twice the document's reference quorum: 100 voters in Monnaie libre. It does not depend on the quorum of an article. It stays between 1% and 50% of the electorate, and is measured once a day.
|
||||||
|
|
||||||
|
**G** is computed so that the share asked, at the usual turnout, is exactly that of the level: `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, where s is the share asked as usual and p̃ the usual turnout divided by W.
|
||||||
|
|
||||||
|
Example: with 7,224 members and a usual turnout of 120 voters, when 120 people vote, it takes 72, 81, 90, 100 or 108 votes in favour, from level 1 to level 5; at level 3, 75% of the 120 voters, i.e. 90. Below the quorum, those missing count as votes against: at level 2 (quorum 12), 9 votes in favour pass with 3 votes against at most; at level 3 (quorum 50), it takes 39 votes in favour, with 11 votes against at most.
|
||||||
|
```
|
||||||
|
|
||||||
|
### N1.1 · Español
|
||||||
|
|
||||||
|
Titre : **Niveles de inercia**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
En cada nivel, el grupo pide un quórum de votantes, y luego una proporción de votos a favor entre ellos (ver «Fórmula del umbral WoT»). Por debajo del quórum, los votantes que faltan cuentan como «no». La proporción depende del nivel y del número de votantes, comparado con la participación habitual del grupo: cuantos más votantes, más baja, hasta la mayoría M cuando vota todo el mundo.
|
||||||
|
|
||||||
|
`Proporción exigida = M + (1 − M) × (1 − (T'/W)^G)`, donde T' es el número de votantes, o el quórum si faltan.
|
||||||
|
|
||||||
|
**Los cinco niveles**: el quórum en Monnaie libre, la proporción exigida cuando el grupo vota como de costumbre, luego cuando vota todo el mundo, y lo que está en ese nivel al inicio.
|
||||||
|
|
||||||
|
- **Nivel 1 · muy baja**: quórum 12; 60 % como de costumbre, 50 % cuando vota todo el mundo. Ningún artículo al inicio.
|
||||||
|
- **Nivel 2 · baja**: quórum 12; 67 % como de costumbre, 50 % cuando vota todo el mundo. Razón de ser, Unicidad de la cuenta, Reciprocidad, y el orden de los artículos de cada sección.
|
||||||
|
- **Nivel 3 · media**: quórum 50; 75 % como de costumbre, 55 % cuando vota todo el mundo. El nivel por defecto: todos los demás artículos, y todo artículo nuevo.
|
||||||
|
- **Nivel 4 · alta**: quórum 50; 83 % como de costumbre, 60 % cuando vota todo el mundo. El título del documento.
|
||||||
|
- **Nivel 5 · muy alta**: quórum 50; 90 % como de costumbre, 66 % cuando vota todo el mundo. Ningún artículo al inicio.
|
||||||
|
|
||||||
|
**La participación habitual** es la mediana del número de votantes de las propuestas del grupo votadas en el último año. Hasta que el grupo haya votado 5 propuestas, vale dos veces el quórum de referencia del documento: 100 votantes en Monnaie libre. No depende del quórum de un artículo. Se mantiene entre el 1 % y el 50 % del electorado, y se mide una vez al día.
|
||||||
|
|
||||||
|
**G** se calcula para que la proporción exigida, con la participación habitual, sea exactamente la del nivel: `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, donde s es la proporción exigida como de costumbre y p̃ la participación habitual dividida por W.
|
||||||
|
|
||||||
|
Ejemplo: con 7224 miembros y una participación habitual de 120 votantes, cuando votan 120 personas, hacen falta 72, 81, 90, 100 o 108 votos a favor, del nivel 1 al nivel 5; en el nivel 3, el 75 % de los 120 votantes, es decir 90. Por debajo del quórum, los que faltan cuentan como «no»: en el nivel 2 (quórum 12), 9 votos a favor pasan con 3 en contra como mucho; en el nivel 3 (quórum 50), hacen falta 39 votos a favor, con 11 en contra como mucho.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Les autres articles, cherchés et laissés
|
||||||
|
|
||||||
|
Recherche dans tout le seed, en fr, en et es : C, un plancher, un minimum de « pour », un seuil, une
|
||||||
|
inertie, le cran de départ de Raison d'être, Unicité du compte et Réciprocité, les nombres 12, 50,
|
||||||
|
100.
|
||||||
|
|
||||||
|
| Article | Ce qu'il dit | Laissé, parce que |
|
||||||
|
|---|---|---|
|
||||||
|
| N1 « Réglage de l'inertie » | un cran de 1 à 5 ; « un article au cran 4 ne descend qu'avec les voix que demande le cran 4 » | vrai : le cran 4 y est un exemple, pas le cran de départ d'un article ; rien sur C |
|
||||||
|
| O1 « Ordonnancement du document » | l'ordre de chaque section au cran 2, le titre au cran 4, « au départ » | vrai (ADR 0009, 0011) |
|
||||||
|
| F1 « Formule de vote » | renvoie à g1vote | rien sur C ni sur un cran |
|
||||||
|
| F1.3 « Processus de dépôt officiel » | « atteint le seuil d'adoption », puis IPFS, `system.remark`, Cesium, Gecko | rien sur C ; mais il décrit un dépôt qui n'est pas celui d'aujourd'hui (pas d'IPFS ni d'ancrage, une publication git) : **hors de ce lot, à reprendre à un prochain tour** si tu le veux |
|
||||||
|
| K1, K2 | 100 certifications, 5 jours… ; le DU | les nombres du protocole et de la monnaie, sans rapport |
|
||||||
|
| E1, E2, I1 | leur propre texte | rien sur leur cran |
|
||||||
|
|
||||||
|
## Points à trancher
|
||||||
|
|
||||||
|
1. **Ton intention de remonter l'inertie** une fois les textes passés : elle n'est pas écrite (ce n'est
|
||||||
|
pas une règle). Si tu veux que F1.2 l'annonce sans la promettre, une phrase vraie suffit : « Leur
|
||||||
|
cran se change ensuite par un vote, comme celui de tout article (voir « Réglage de l'inertie »). »
|
||||||
|
2. **« Propositions seed »** dans un texte voté : c'est le mot de l'interface (« Proposition seed »,
|
||||||
|
« Seed proposal », « Propuesta seed »). Autre choix : « propositions mises en avant ».
|
||||||
|
3. **L'exemple de N1.1** (7 224 membres, 120 votants) reste celui de M5 et des tests (le vote
|
||||||
|
forgeron de 2024) : une illustration, pas la toile d'aujourd'hui (5 707 membres en production le
|
||||||
|
02/11). À ce W et à la participation de référence, les deux exemples sous le quorum donnent les
|
||||||
|
mêmes nombres de « pour » : 9 au cran 2, 39 au cran 3 (`crates/ld-engine/src/inertia.rs`,
|
||||||
|
`the_control_figures_of_the_quorum`). À retirer si tu ne veux pas de chiffre daté dans un texte voté.
|
||||||
|
4. **Le groupe d'essai** (quorum 1 partout) n'est pas nommé : les textes donnent les quorums « dans
|
||||||
|
Monnaie libre », comme F1.2 donnait C = 50.
|
||||||
|
5. **F1.1 a changé de plan** : le sens d'abord, la formule ensuite, les lettres en liste (la liste
|
||||||
|
d'aujourd'hui ne s'affiche pas comme liste, faute de ligne vide). B y est « la base » (W l'exposant :
|
||||||
|
« exposant de base » était faux), M n'y est plus `majority_pct / 100`, et « Seuil » est traduit en
|
||||||
|
anglais et en espagnol (*Threshold*, *Umbral*) : trois défauts relevés par M5 (`textes-regles-seed.md`,
|
||||||
|
point 7).
|
||||||
|
6. **N1.1, la formule de G** : p̃ y est « la participation habituelle divisée par W » (relevé par M5 à
|
||||||
|
la mise en ligne : G se calcule sur une part de l'électorat, pas sur un nombre de votants).
|
||||||
|
7. **Ce qui deviendra caduc une fois en ligne**, non touché par ce lot : `docs/adr/0013-quorum.md:133-135`
|
||||||
|
(« Le texte F1.1 du seed […] et N1.1 […] décrivent encore C ») et
|
||||||
|
`docs/adr/0011-propositions-seed.md:89-90` (F1.2 et N1.1 disent encore Unicité et Réciprocité au
|
||||||
|
cran 4).
|
||||||
|
|
||||||
|
## Les chiffres et leur source
|
||||||
|
|
||||||
|
Chaque nombre est vérifié par `every_number_of_the_quorum_texts_is_the_engine_s`
|
||||||
|
(`crates/ld-server/tests/seed_quorum_texts.rs`), qui le calcule ainsi :
|
||||||
|
|
||||||
|
| Chiffre | Où | Source |
|
||||||
|
|---|---|---|
|
||||||
|
| `T' = max(T, Q)` ; `Seuil = B^W + (M + (1 − M) × (1 − (T'/W)^G)) × T'` | F1.1 | `threshold::judged_voters`, `required_ratio`, `wot_threshold` (`crates/ld-engine/src/threshold.rs:57-85`) ; évaluées telles qu'écrites |
|
||||||
|
| `Part exigée = M + (1 − M) × (1 − (T'/W)^G)` | N1.1 | `threshold::required_ratio` ; évaluée telle qu'écrite |
|
||||||
|
| `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)` | N1.1 | `inertia::gradient` (`crates/ld-engine/src/inertia.rs:190-197`) ; évaluée telle qu'écrite |
|
||||||
|
| Quorums 12, 12, 50, 50, 50, du cran 1 au cran 5 | F1.2, N1.1 | le seed, `instances[monnaie-libre].quorums` ; `InertiaLevel::LOWEST`, `HIGHEST` |
|
||||||
|
| Jamais moins de 2 | F1.2 | `inertia::quorum_bounds(W, false)` |
|
||||||
|
| Qui change le quorum, avant le premier vote | F1.2 | `trustin_access::sets_quorum` (`crates/trustin-access/src/lib.rs:204-217`) ; ADR 0013 |
|
||||||
|
| W relu toutes les 6 heures | F1.2 | `wot::REFRESH` (`crates/ld-server/src/wot.rs:13-14`) |
|
||||||
|
| B = 0,1 | F1.2 | `FormulaParams::DEFAULT_BASE` |
|
||||||
|
| Cran 3 par défaut : M = 55 %, quorum 50, 75 % | F1.2, N1.1 | `InertiaLevel::DEFAULT`, `majority_pct`, `share_pct` ; le quorum du cran |
|
||||||
|
| M = 50, 50, 55, 60, 66 % ; s = 60, 67, 75, 83, 90 % | N1.1 | `InertiaLevel::majority_pct`, `share_pct` |
|
||||||
|
| Propositions seed au cran 2 | F1.2, N1.1 | `seed::SEED_PROPOSAL_LEVEL` ; les versions `seedProposal` du seed, leurs titres dans chaque langue |
|
||||||
|
| Le titre au cran 4, l'ordre de chaque section au cran 2 | F1.2, N1.1 | le seed, `modules.title.inertia`, `modules.order.inertia` |
|
||||||
|
| Tous les autres articles au cran 3 ; rien aux crans 1 et 5 | F1.2, N1.1 | le seed : chaque `inertia` (toutes égales, vérifié) |
|
||||||
|
| 5 propositions ; 100 votants (deux fois le quorum de référence) | N1.1 | `Participation::MIN_SAMPLE` ; `Participation::of(W, voteFloor, None, 0).voters(W)`, `voteFloor` = 50 |
|
||||||
|
| Entre 1 % et 50 % | N1.1 | `Participation::LOWEST`, `HIGHEST` |
|
||||||
|
| Depuis un an ; une fois par jour | N1.1 | `participation::WINDOW_DAYS` = 365, `EVERY_HOURS` = 24 |
|
||||||
|
| 72, 81, 90, 100, 108 voix pour à 120 votants | N1.1 | `Scale::required(120, k, Q_k)`, W 7 224, médiane 120 ; déjà `inertia.rs`, `the_values_shown_to_yvv` |
|
||||||
|
| 75 % des 120 votants, soit 90 | N1.1 | la part du cran 3 × 120, égale au seuil du moteur (vérifié) |
|
||||||
|
| Cran 2 (quorum 12) : 9 « pour », 3 « contre » au plus | N1.1 | `threshold::yes_needed(0)`, puis `no_needed(9) − 1`, W 7 224, médiane 120 |
|
||||||
|
| Cran 3 (quorum 50) : 39 « pour », 11 « contre » au plus | N1.1 | idem ; les deux exemples restent sous le quorum (vérifié) |
|
||||||
|
|
||||||
|
Les règles sans chiffre :
|
||||||
|
|
||||||
|
| Règle | Où | Source |
|
||||||
|
|---|---|---|
|
||||||
|
| Sous le quorum, les votants qui manquent comptent comme « non » | F1.1, F1.2, N1.1 | `threshold::judged_voters` ; ADR 0013 |
|
||||||
|
| Au-delà du quorum, la part du cran parmi tous les votants | F1.1, N1.1 | `threshold::wot_threshold` ; ADR 0013 (« la courbe d'aujourd'hui, après le quorum ») |
|
||||||
|
| Une version passe quand ses « pour » atteignent le seuil arrondi au-dessus | F1.1 | `threshold::passes`, `clamp_ceil` |
|
||||||
|
| La participation habituelle ne dépend pas du quorum d'un article | N1.1 | `inertia::Participation::reference_ratio` (le quorum de référence du document) ; ADR 0013 |
|
||||||
|
| Au départ, aucun article en vigueur | F1.2 | ADR 0011 ; le seed (aucune version `in_force` hors modules) |
|
||||||
|
|
||||||
|
## Les textes dans le seed
|
||||||
|
|
||||||
|
Déjà collés (le `_source` du seed le dit) ; une base neuve naît avec eux, et l'amendement les écrit
|
||||||
|
dans une base existante. Rien à recopier.
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"_source": "maquette/frontend/app/data/seeds/duniter-g1.bundle.json (doc engagement-certification, 33 clauses + 2 variantes). Aucun article en vigueur au départ. I1, E1, E2 : Propositions seed (« seedProposal »), ouvertes, mises en avant pour amorcer le vote, sans niveau d'inertie ici : elles démarrent au cran SEED_PROPOSAL_LEVEL (crates/ld-server/src/seed.rs : 2 « basse », choix d'Yvv du 2026-11-02, qui remontera leur inertie par un vote une fois les textes passés ; ADR 0011). Tant qu'aucun article n'est en vigueur, la publication attend le premier texte. Tout le reste : propositions ouvertes à 0 vote (Yvv, 2026-10-21), inertie 3 / 5. I1 « Raison d'être » remplace le Préambule (texte d'Yvv, revue du 2026-10-30). Modules : le titre du document à 4, l'ordre des articles de chaque section à 2, en vigueur sans vote (seed a minima ; revue du 2026-10-30, ADR 0009). F1.2, N1, N1.1, O1 réécrits selon les règles appliquées (Yvv, 2026-11-02). Quorums par cran (« quorums », ADR 0013, Yvv, 2026-11-02 et 03) : 12 · 12 · 50 · 50 · 50 dans Monnaie libre, 1 partout dans le groupe d'essai ; sous le quorum, les votants qui manquent comptent comme « non » ; voteFloor reste le quorum de référence de la participation p0 = 2 × voteFloor / W. Une base existante reçoit ces changements par des amendements gardés (seed_amend, seed_proposal_amend, quorum_amend), jamais en réinstanciant le document.",
|
"_source": "maquette/frontend/app/data/seeds/duniter-g1.bundle.json (doc engagement-certification, 33 clauses + 2 variantes). Aucun article en vigueur au départ. I1, E1, E2 : Propositions seed (« seedProposal »), ouvertes, mises en avant pour amorcer le vote, sans niveau d'inertie ici : elles démarrent au cran SEED_PROPOSAL_LEVEL (crates/ld-server/src/seed.rs : 2 « basse », choix d'Yvv du 2026-11-02, qui remontera leur inertie par un vote une fois les textes passés ; ADR 0011). Tant qu'aucun article n'est en vigueur, la publication attend le premier texte. Tout le reste : propositions ouvertes à 0 vote (Yvv, 2026-10-21), inertie 3 / 5. I1 « Raison d'être » remplace le Préambule (texte d'Yvv, revue du 2026-10-30). Modules : le titre du document à 4, l'ordre des articles de chaque section à 2, en vigueur sans vote (seed a minima ; revue du 2026-10-30, ADR 0009). F1.2, N1, N1.1, O1 réécrits selon les règles appliquées (Yvv, 2026-11-02). Quorums par cran (« quorums », ADR 0013, Yvv, 2026-11-02 et 03) : 12 · 12 · 50 · 50 · 50 dans Monnaie libre, 1 partout dans le groupe d'essai ; sous le quorum, les votants qui manquent comptent comme « non » ; voteFloor reste le quorum de référence de la participation p0 = 2 × voteFloor / W. Une base existante reçoit ces changements par des amendements gardés (seed_amend, seed_proposal_amend, quorum_amend), jamais en réinstanciant le document. F1.1, F1.2 et N1.1 réécrits pour le quorum par cran (ADR 0013 ; Yvv, 2026-11-02 et 03 ; lot T, textes relus dans docs/chantiers/acte-certification/textes-quorum-seed.md) : sous le quorum, les votants qui manquent comptent comme « non » ; au-delà, la part du cran.",
|
||||||
"slug": "acte-engagement-certification",
|
"slug": "acte-engagement-certification",
|
||||||
"title": {
|
"title": {
|
||||||
"fr": "Acte d'engagement Certification",
|
"fr": "Acte d'engagement Certification",
|
||||||
@@ -672,9 +672,9 @@
|
|||||||
"es": "Fórmula del umbral WoT"
|
"es": "Fórmula del umbral WoT"
|
||||||
},
|
},
|
||||||
"body": {
|
"body": {
|
||||||
"fr": "**Seuil = C + B^W + (M + (1-M) × (1 - (T/W)^G)) × max(0, T-C)**\n\nOù :\n- **C** = constante de base (plancher fixe)\n- **B** = exposant de base (B^W tend vers 0)\n- **W** = taille de la toile de confiance (électeurs éligibles)\n- **T** = total des votes exprimés (pour + contre)\n- **M** = ratio de majorité cible (M = majority_pct / 100)\n- **G** = exposant du gradient d'inertie\n\n**Comportement :** Faible participation → quasi-unanimité requise. Forte participation → majorité simple M suffit.",
|
"fr": "Le seuil est le nombre de voix pour qu'il faut à une version pour entrer en vigueur. Sous le quorum Q, les votants qui manquent comptent comme « non » : le seuil se calcule comme si Q personnes avaient voté. Au-delà, il faut la part de « pour » que demande le cran d'inertie de l'article, parmi tous les votants : presque l'unanimité quand peu de gens votent, la majorité M quand tout le monde vote.\n\n`T' = max(T, Q)`\n`Seuil = B^W + (M + (1 − M) × (1 − (T'/W)^G)) × T'`\n\nUne version passe quand ses voix pour atteignent le seuil, arrondi à l'entier au-dessus.\n\n- **T** : les votes exprimés sur la version, pour et contre.\n- **Q** : le quorum de l'article ; **T'** : les votants retenus, T, ou Q s'il en manque.\n- **W** : le nombre de membres de la toile de confiance Ğ1.\n- **B** : la base ; à la puissance W, elle est si petite qu'elle ne compte pas.\n- **M** : la majorité demandée quand tout le monde vote.\n- **G** : le gradient : plus il est grand, plus la part demandée monte quand peu de gens votent.\n\nLeurs valeurs sont dans « Paramètres par défaut » et « Niveaux d'inertie ».",
|
||||||
"en": "**Seuil = C + B^W + (M + (1-M) × (1 - (T/W)^G)) × max(0, T-C)**\n\nWhere:\n- **C** = base constant (fixed floor)\n- **B** = base exponent (B^W tends towards 0)\n- **W** = size of the web of trust (eligible voters)\n- **T** = total votes cast (for + against)\n- **M** = target majority ratio (M = majority_pct / 100)\n- **G** = inertia gradient exponent\n\n**Behaviour:** Low participation → near-unanimity required. High participation → simple majority M is enough.",
|
"en": "The threshold is the number of votes in favour a version needs to come into force. Below the quorum Q, the voters who are missing count as votes against: the threshold is worked out as if Q people had voted. Beyond it, it takes the share of votes in favour that the article's level of inertia asks for, among all the voters: near-unanimity when few people vote, the majority M when everyone votes.\n\n`T' = max(T, Q)`\n`Threshold = B^W + (M + (1 − M) × (1 − (T'/W)^G)) × T'`\n\nA version passes when its votes in favour reach the threshold, rounded up to a whole number.\n\n- **T**: the votes cast on the version, for and against.\n- **Q**: the article's quorum; **T'**: the voters counted, T, or Q if some are missing.\n- **W**: the number of members of the Ğ1 web of trust.\n- **B**: the base; raised to the power W, it is so small that it does not count.\n- **M**: the majority asked for when everyone votes.\n- **G**: the gradient: the larger it is, the higher the share asked for when few people vote.\n\nTheir values are in “Default parameters” and “Inertia levels”.",
|
||||||
"es": "**Seuil = C + B^W + (M + (1-M) × (1 - (T/W)^G)) × max(0, T-C)**\n\nDonde:\n- **C** = constante de base (mínimo fijo)\n- **B** = exponente de base (B^W tiende a 0)\n- **W** = tamaño de la red de confianza (votantes habilitados)\n- **T** = total de votos emitidos (a favor + en contra)\n- **M** = proporción de mayoría objetivo (M = majority_pct / 100)\n- **G** = exponente del gradiente de inercia\n\n**Comportamiento:** Baja participación → se requiere casi unanimidad. Alta participación → basta la mayoría simple M."
|
"es": "El umbral es el número de votos a favor que necesita una versión para entrar en vigor. Por debajo del quórum Q, los votantes que faltan cuentan como «no»: el umbral se calcula como si hubieran votado Q personas. Más allá, hace falta la proporción de votos a favor que pide el nivel de inercia del artículo, entre todos los votantes: casi la unanimidad cuando vota poca gente, la mayoría M cuando vota todo el mundo.\n\n`T' = max(T, Q)`\n`Umbral = B^W + (M + (1 − M) × (1 − (T'/W)^G)) × T'`\n\nUna versión pasa cuando sus votos a favor alcanzan el umbral, redondeado al entero superior.\n\n- **T**: los votos emitidos sobre la versión, a favor y en contra.\n- **Q**: el quórum del artículo; **T'**: los votantes contados, T, o Q si faltan.\n- **W**: el número de miembros de la red de confianza Ğ1.\n- **B**: la base; elevada a la potencia W, es tan pequeña que no cuenta.\n- **M**: la mayoría pedida cuando vota todo el mundo.\n- **G**: el gradiente: cuanto mayor es, más alta es la proporción pedida cuando vota poca gente.\n\nSus valores están en «Parámetros por defecto» y «Niveles de inercia»."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -694,9 +694,9 @@
|
|||||||
"es": "Parámetros por defecto"
|
"es": "Parámetros por defecto"
|
||||||
},
|
},
|
||||||
"body": {
|
"body": {
|
||||||
"fr": "Les valeurs des lettres de la formule (voir « Formule du seuil WoT ») et la façon dont le vote se déroule.\n\n**Un vote permanent, sans durée**\n\n- Chaque version se vote à tout moment, sans échéance. Chacun peut changer ou retirer son vote : le dernier fait foi.\n- Une proposition entre en vigueur dès qu'elle atteint son seuil. S'il y a déjà un texte en vigueur, il lui faut en plus une marge (voix pour moins voix exigées) plus large que celle de ce texte. Le texte remplacé redevient une proposition, avec ses votes : il peut revenir.\n- Si plusieurs propositions y parviennent, celle qui a la marge la plus large entre en vigueur.\n\n**Les lettres**\n\n- **W** : le nombre de membres de la toile de confiance Ğ1, relu sur la chaîne toutes les 6 heures.\n- **T** : les votes exprimés sur la version, pour et contre.\n- **C = 50** : le nombre minimal de votes « pour » dans Monnaie libre. Sous 50 voix pour, rien ne passe : une seule voix ne fait jamais passer un texte.\n- **B = 0,1** : dans la toile de confiance, `B^W` est si petit qu'il ne compte pas.\n- **M** et **G** : donnés par le cran d'inertie de l'article (voir « Niveaux d'inertie »). G n'est pas fixé à la main : il se déduit de la participation habituelle du groupe.\n\n**Par défaut**\n\n- Le cran par défaut est le 3 (moyenne) : M = 55 %, et le groupe demande 75 % de « pour » au-delà de C quand il vote comme d'habitude. Tout nouvel article commence à ce cran.\n- Au départ, Unicité du compte, Réciprocité et le titre du document sont au cran 4 (haute), l'ordre des articles de chaque section au cran 2 (basse), tous les autres articles au cran 3.",
|
"fr": "Les valeurs des lettres de la formule (voir « Formule du seuil WoT ») et la façon dont le vote se déroule.\n\n**Un vote permanent, sans durée**\n\n- Chaque version se vote à tout moment, sans échéance. Chacun peut changer ou retirer son vote : le dernier fait foi.\n- Une proposition entre en vigueur dès qu'elle atteint son seuil. S'il y a déjà un texte en vigueur, il lui faut en plus une marge (voix pour moins voix exigées) plus large que celle de ce texte. Le texte remplacé redevient une proposition, avec ses votes : il peut revenir.\n- Si plusieurs propositions y parviennent, celle qui a la marge la plus large entre en vigueur.\n\n**Les lettres**\n\n- **W** : le nombre de membres de la toile de confiance Ğ1, relu sur la chaîne toutes les 6 heures.\n- **T** : les votes exprimés sur la version, pour et contre.\n- **Q**, le quorum : sous ce nombre de votants, ceux qui manquent comptent comme « non ». Chaque article prend celui de son cran : 12, 12, 50, 50 et 50 dans Monnaie libre, du cran 1 au cran 5. L'auteur de la première version d'un article, un administrateur pour les textes de départ, peut en fixer un autre tant que personne n'a voté sur l'article ; ensuite, il ne change plus. Dans Monnaie libre, jamais moins de 2 : une seule voix ne fait jamais passer un texte.\n- **B = 0,1** : dans la toile de confiance, `B^W` est si petit qu'il ne compte pas.\n- **M** et **G** : donnés par le cran d'inertie de l'article (voir « Niveaux d'inertie »). G n'est pas fixé à la main : il se déduit de la participation habituelle du groupe.\n\n**Par défaut**\n\n- Le cran par défaut est le 3 (moyenne) : M = 55 %, un quorum de 50 dans Monnaie libre, et le groupe demande 75 % de « pour » quand il vote comme d'habitude. Tout nouvel article commence à ce cran.\n- Au départ, aucun article n'est en vigueur. Raison d'être, Unicité du compte et Réciprocité sont des Propositions seed, mises en avant pour lancer le vote, au cran 2 (basse). Le titre du document est au cran 4 (haute), l'ordre des articles de chaque section au cran 2, tous les autres articles au cran 3.",
|
||||||
"en": "The values of the letters of the formula (see “WoT threshold formula”) and how the vote runs.\n\n**A permanent vote, with no duration**\n\n- Every version can be voted on at any time, with no deadline. Anyone can change or withdraw their vote: the last one is the one that counts.\n- A proposal comes into force as soon as it reaches its threshold. If a text is already in force, it also needs a wider margin (votes in favour minus votes required) than that text has. The replaced text becomes a proposal again, with its votes: it can come back.\n- If several proposals get there, the one with the widest margin comes into force.\n\n**The letters**\n\n- **W**: the number of members of the Ğ1 web of trust, read again from the chain every 6 hours.\n- **T**: the votes cast on the version, for and against.\n- **C = 50**: the minimum number of votes in favour in Monnaie libre. Below 50 votes in favour, nothing passes: a single vote never passes a text.\n- **B = 0.1**: in the web of trust, `B^W` is so small that it does not count.\n- **M** and **G**: given by the article's level of inertia (see “Inertia levels”). G is not set by hand: it follows from the group's usual turnout.\n\n**By default**\n\n- The default level is 3 (medium): M = 55%, and the group asks for 75% of votes in favour beyond C when it votes as usual. Every new article starts at this level.\n- At the start, Uniqueness of the account, Reciprocity and the title of the document are at level 4 (high), the order of the articles of each section at level 2 (low), all other articles at level 3.",
|
"en": "The values of the letters of the formula (see “WoT threshold formula”) and how the vote runs.\n\n**A permanent vote, with no duration**\n\n- Every version can be voted on at any time, with no deadline. Anyone can change or withdraw their vote: the last one is the one that counts.\n- A proposal comes into force as soon as it reaches its threshold. If a text is already in force, it also needs a wider margin (votes in favour minus votes required) than that text has. The replaced text becomes a proposal again, with its votes: it can come back.\n- If several proposals get there, the one with the widest margin comes into force.\n\n**The letters**\n\n- **W**: the number of members of the Ğ1 web of trust, read again from the chain every 6 hours.\n- **T**: the votes cast on the version, for and against.\n- **Q**, the quorum: below this number of voters, those who are missing count as votes against. Each article takes the one of its level: 12, 12, 50, 50 and 50 in Monnaie libre, from level 1 to level 5. The author of the first version of an article, an administrator for the starting texts, can set another one as long as nobody has voted on the article; after that, it no longer changes. In Monnaie libre, never less than 2: a single vote never passes a text.\n- **B = 0.1**: in the web of trust, `B^W` is so small that it does not count.\n- **M** and **G**: given by the article's level of inertia (see “Inertia levels”). G is not set by hand: it follows from the group's usual turnout.\n\n**By default**\n\n- The default level is 3 (medium): M = 55%, a quorum of 50 in Monnaie libre, and the group asks for 75% of votes in favour when it votes as usual. Every new article starts at this level.\n- At the start, no article is in force. Purpose, Uniqueness of the account and Reciprocity are seed proposals, put forward to start the vote, at level 2 (low). The title of the document is at level 4 (high), the order of the articles of each section at level 2, all other articles at level 3.",
|
||||||
"es": "Los valores de las letras de la fórmula (ver «Fórmula del umbral WoT») y cómo se desarrolla la votación.\n\n**Una votación permanente, sin duración**\n\n- Cada versión se vota en cualquier momento, sin plazo. Cada cual puede cambiar o retirar su voto: el último es el que cuenta.\n- Una propuesta entra en vigor en cuanto alcanza su umbral. Si ya hay un texto en vigor, necesita además un margen (votos a favor menos votos exigidos) más amplio que el de ese texto. El texto sustituido vuelve a ser una propuesta, con sus votos: puede volver.\n- Si varias propuestas lo consiguen, entra en vigor la que tiene el margen más amplio.\n\n**Las letras**\n\n- **W**: el número de miembros de la red de confianza Ğ1, leído de nuevo en la cadena cada 6 horas.\n- **T**: los votos emitidos sobre la versión, a favor y en contra.\n- **C = 50**: el número mínimo de votos a favor en Monnaie libre. Con menos de 50 votos a favor, nada pasa: un solo voto nunca hace pasar un texto.\n- **B = 0,1**: en la red de confianza, `B^W` es tan pequeño que no cuenta.\n- **M** y **G**: los da el nivel de inercia del artículo (ver «Niveles de inercia»). G no se fija a mano: se deduce de la participación habitual del grupo.\n\n**Por defecto**\n\n- El nivel por defecto es el 3 (media): M = 55 %, y el grupo pide un 75 % de votos a favor más allá de C cuando vota como de costumbre. Todo artículo nuevo empieza en este nivel.\n- Al inicio, Unicidad de la cuenta, Reciprocidad y el título del documento están en el nivel 4 (alta), el orden de los artículos de cada sección en el nivel 2 (baja), todos los demás artículos en el nivel 3."
|
"es": "Los valores de las letras de la fórmula (ver «Fórmula del umbral WoT») y cómo se desarrolla la votación.\n\n**Una votación permanente, sin duración**\n\n- Cada versión se vota en cualquier momento, sin plazo. Cada cual puede cambiar o retirar su voto: el último es el que cuenta.\n- Una propuesta entra en vigor en cuanto alcanza su umbral. Si ya hay un texto en vigor, necesita además un margen (votos a favor menos votos exigidos) más amplio que el de ese texto. El texto sustituido vuelve a ser una propuesta, con sus votos: puede volver.\n- Si varias propuestas lo consiguen, entra en vigor la que tiene el margen más amplio.\n\n**Las letras**\n\n- **W**: el número de miembros de la red de confianza Ğ1, leído de nuevo en la cadena cada 6 horas.\n- **T**: los votos emitidos sobre la versión, a favor y en contra.\n- **Q**, el quórum: por debajo de este número de votantes, los que faltan cuentan como «no». Cada artículo toma el de su nivel: 12, 12, 50, 50 y 50 en Monnaie libre, del nivel 1 al nivel 5. El autor de la primera versión de un artículo, un administrador para los textos de partida, puede fijar otro mientras nadie haya votado sobre el artículo; después, ya no cambia. En Monnaie libre, nunca menos de 2: un solo voto nunca hace pasar un texto.\n- **B = 0,1**: en la red de confianza, `B^W` es tan pequeño que no cuenta.\n- **M** y **G**: los da el nivel de inercia del artículo (ver «Niveles de inercia»). G no se fija a mano: se deduce de la participación habitual del grupo.\n\n**Por defecto**\n\n- El nivel por defecto es el 3 (media): M = 55 %, un quórum de 50 en Monnaie libre, y el grupo pide un 75 % de votos a favor cuando vota como de costumbre. Todo artículo nuevo empieza en este nivel.\n- Al inicio, ningún artículo está en vigor. Razón de ser, Unicidad de la cuenta y Reciprocidad son Propuestas seed, puestas en primer plano para lanzar la votación, en el nivel 2 (baja). El título del documento está en el nivel 4 (alta), el orden de los artículos de cada sección en el nivel 2, todos los demás artículos en el nivel 3."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -760,9 +760,9 @@
|
|||||||
"es": "Niveles de inercia"
|
"es": "Niveles de inercia"
|
||||||
},
|
},
|
||||||
"body": {
|
"body": {
|
||||||
"fr": "À chaque cran, le groupe demande d'abord C voix pour, puis une part de « pour » parmi les votes au-delà de C. Cette part dépend du cran et du nombre de votants, comparé à la participation habituelle du groupe : plus il y a de votants, plus elle baisse, jusqu'à la majorité M quand tout le monde vote.\n\n`Part exigée = M + (1 − M) × (1 − (T/W)^G)`\n\n**Les cinq crans** : la part exigée quand le groupe vote comme d'habitude, puis quand tout le monde vote, et ce qui est à ce cran au départ.\n\n- **Cran 1 · très basse** : 60 % comme d'habitude, 50 % quand tout le monde vote. Aucun article au départ.\n- **Cran 2 · basse** : 67 % comme d'habitude, 50 % quand tout le monde vote. L'ordre des articles de chaque section.\n- **Cran 3 · moyenne** : 75 % comme d'habitude, 55 % quand tout le monde vote. Le cran par défaut : tous les autres articles, et chaque nouvel article.\n- **Cran 4 · haute** : 83 % comme d'habitude, 60 % quand tout le monde vote. Unicité du compte, Réciprocité, le titre du document.\n- **Cran 5 · très haute** : 90 % comme d'habitude, 66 % quand tout le monde vote. Aucun article au départ.\n\n**La participation habituelle** est la médiane du nombre de votants des propositions du groupe votées depuis un an. Tant que le groupe n'a pas voté sur 5 propositions, elle vaut deux fois C : 100 votants dans Monnaie libre. Elle reste entre 1 % et 50 % de l'électorat, et se mesure une fois par jour.\n\n**G** se calcule pour que la part exigée, à la participation habituelle p̃, soit exactement celle du cran : `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, où s est la part exigée comme d'habitude.\n\nExemple : avec 7 224 membres et une participation habituelle de 120 votants, quand 120 personnes votent, il faut 92, 97, 103, 109 ou 113 voix pour, du cran 1 au cran 5. Au cran 3 : 50 voix pour, plus 75 % des 70 autres votes, soit 102,5, arrondi à 103.",
|
"fr": "À chaque cran, le groupe demande un quorum de votants, puis une part de « pour » parmi eux (voir « Formule du seuil WoT »). Sous le quorum, les votants qui manquent comptent comme « non ». La part dépend du cran et du nombre de votants, comparé à la participation habituelle du groupe : plus il y a de votants, plus elle baisse, jusqu'à la majorité M quand tout le monde vote.\n\n`Part exigée = M + (1 − M) × (1 − (T'/W)^G)`, où T' est le nombre de votants, ou le quorum s'il en manque.\n\n**Les cinq crans** : le quorum dans Monnaie libre, la part exigée quand le groupe vote comme d'habitude, puis quand tout le monde vote, et ce qui est à ce cran au départ.\n\n- **Cran 1 · très basse** : quorum 12 ; 60 % comme d'habitude, 50 % quand tout le monde vote. Aucun article au départ.\n- **Cran 2 · basse** : quorum 12 ; 67 % comme d'habitude, 50 % quand tout le monde vote. Raison d'être, Unicité du compte, Réciprocité, et l'ordre des articles de chaque section.\n- **Cran 3 · moyenne** : quorum 50 ; 75 % comme d'habitude, 55 % quand tout le monde vote. Le cran par défaut : tous les autres articles, et chaque nouvel article.\n- **Cran 4 · haute** : quorum 50 ; 83 % comme d'habitude, 60 % quand tout le monde vote. Le titre du document.\n- **Cran 5 · très haute** : quorum 50 ; 90 % comme d'habitude, 66 % quand tout le monde vote. Aucun article au départ.\n\n**La participation habituelle** est la médiane du nombre de votants des propositions du groupe votées depuis un an. Tant que le groupe n'a pas voté sur 5 propositions, elle vaut deux fois le quorum de référence du document : 100 votants dans Monnaie libre. Elle ne dépend pas du quorum d'un article. Elle reste entre 1 % et 50 % de l'électorat, et se mesure une fois par jour.\n\n**G** se calcule pour que la part exigée, à la participation habituelle, soit exactement celle du cran : `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, où s est la part exigée comme d'habitude et p̃ la participation habituelle divisée par W.\n\nExemple : avec 7 224 membres et une participation habituelle de 120 votants, quand 120 personnes votent, il faut 72, 81, 90, 100 ou 108 voix pour, du cran 1 au cran 5 ; au cran 3, 75 % des 120 votants, soit 90. Sous le quorum, les absents comptent comme « non » : au cran 2 (quorum 12), 9 voix pour passent avec 3 « contre » au plus ; au cran 3 (quorum 50), il faut 39 voix pour, avec 11 « contre » au plus.",
|
||||||
"en": "At each level, the group first asks for C votes in favour, then for a share of votes in favour among the votes beyond C. That share depends on the level and on the number of voters, compared with the group's usual turnout: the more voters, the lower it gets, down to the majority M when everyone votes.\n\n`Share asked = M + (1 − M) × (1 − (T/W)^G)`\n\n**The five levels**: the share asked when the group votes as usual, then when everyone votes, and what is at that level at the start.\n\n- **Level 1 · very low**: 60% as usual, 50% when everyone votes. No article at the start.\n- **Level 2 · low**: 67% as usual, 50% when everyone votes. The order of the articles of each section.\n- **Level 3 · medium**: 75% as usual, 55% when everyone votes. The default level: all other articles, and every new article.\n- **Level 4 · high**: 83% as usual, 60% when everyone votes. Uniqueness of the account, Reciprocity, the title of the document.\n- **Level 5 · very high**: 90% as usual, 66% when everyone votes. No article at the start.\n\n**The usual turnout** is the median number of voters on the group's proposals voted on over the past year. Until the group has voted on 5 proposals, it is twice C: 100 voters in Monnaie libre. It stays between 1% and 50% of the electorate, and is measured once a day.\n\n**G** is computed so that the share asked, at the usual turnout p̃, is exactly that of the level: `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, where s is the share asked as usual.\n\nExample: with 7,224 members and a usual turnout of 120 voters, when 120 people vote, it takes 92, 97, 103, 109 or 113 votes in favour, from level 1 to level 5. At level 3: 50 votes in favour, plus 75% of the 70 other votes, i.e. 102.5, rounded up to 103.",
|
"en": "At each level, the group asks for a quorum of voters, then for a share of votes in favour among them (see “WoT threshold formula”). Below the quorum, the voters who are missing count as votes against. The share depends on the level and on the number of voters, compared with the group's usual turnout: the more voters, the lower it gets, down to the majority M when everyone votes.\n\n`Share asked = M + (1 − M) × (1 − (T'/W)^G)`, where T' is the number of voters, or the quorum if some are missing.\n\n**The five levels**: the quorum in Monnaie libre, the share asked when the group votes as usual, then when everyone votes, and what is at that level at the start.\n\n- **Level 1 · very low**: quorum 12; 60% as usual, 50% when everyone votes. No article at the start.\n- **Level 2 · low**: quorum 12; 67% as usual, 50% when everyone votes. Purpose, Uniqueness of the account, Reciprocity, and the order of the articles of each section.\n- **Level 3 · medium**: quorum 50; 75% as usual, 55% when everyone votes. The default level: all other articles, and every new article.\n- **Level 4 · high**: quorum 50; 83% as usual, 60% when everyone votes. The title of the document.\n- **Level 5 · very high**: quorum 50; 90% as usual, 66% when everyone votes. No article at the start.\n\n**The usual turnout** is the median number of voters on the group's proposals voted on over the past year. Until the group has voted on 5 proposals, it is twice the document's reference quorum: 100 voters in Monnaie libre. It does not depend on the quorum of an article. It stays between 1% and 50% of the electorate, and is measured once a day.\n\n**G** is computed so that the share asked, at the usual turnout, is exactly that of the level: `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, where s is the share asked as usual and p̃ the usual turnout divided by W.\n\nExample: with 7,224 members and a usual turnout of 120 voters, when 120 people vote, it takes 72, 81, 90, 100 or 108 votes in favour, from level 1 to level 5; at level 3, 75% of the 120 voters, i.e. 90. Below the quorum, those missing count as votes against: at level 2 (quorum 12), 9 votes in favour pass with 3 votes against at most; at level 3 (quorum 50), it takes 39 votes in favour, with 11 votes against at most.",
|
||||||
"es": "En cada nivel, el grupo pide primero C votos a favor, y luego una proporción de votos a favor entre los votos más allá de C. Esa proporción depende del nivel y del número de votantes, comparado con la participación habitual del grupo: cuantos más votantes, más baja, hasta la mayoría M cuando vota todo el mundo.\n\n`Proporción exigida = M + (1 − M) × (1 − (T/W)^G)`\n\n**Los cinco niveles**: la proporción exigida cuando el grupo vota como de costumbre, luego cuando vota todo el mundo, y lo que está en ese nivel al inicio.\n\n- **Nivel 1 · muy baja**: 60 % como de costumbre, 50 % cuando vota todo el mundo. Ningún artículo al inicio.\n- **Nivel 2 · baja**: 67 % como de costumbre, 50 % cuando vota todo el mundo. El orden de los artículos de cada sección.\n- **Nivel 3 · media**: 75 % como de costumbre, 55 % cuando vota todo el mundo. El nivel por defecto: todos los demás artículos, y todo artículo nuevo.\n- **Nivel 4 · alta**: 83 % como de costumbre, 60 % cuando vota todo el mundo. Unicidad de la cuenta, Reciprocidad, el título del documento.\n- **Nivel 5 · muy alta**: 90 % como de costumbre, 66 % cuando vota todo el mundo. Ningún artículo al inicio.\n\n**La participación habitual** es la mediana del número de votantes de las propuestas del grupo votadas en el último año. Hasta que el grupo haya votado 5 propuestas, vale dos veces C: 100 votantes en Monnaie libre. Se mantiene entre el 1 % y el 50 % del electorado, y se mide una vez al día.\n\n**G** se calcula para que la proporción exigida, con la participación habitual p̃, sea exactamente la del nivel: `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, donde s es la proporción exigida como de costumbre.\n\nEjemplo: con 7224 miembros y una participación habitual de 120 votantes, cuando votan 120 personas, hacen falta 92, 97, 103, 109 o 113 votos a favor, del nivel 1 al nivel 5. En el nivel 3: 50 votos a favor, más el 75 % de los otros 70 votos, es decir 102,5, redondeado a 103."
|
"es": "En cada nivel, el grupo pide un quórum de votantes, y luego una proporción de votos a favor entre ellos (ver «Fórmula del umbral WoT»). Por debajo del quórum, los votantes que faltan cuentan como «no». La proporción depende del nivel y del número de votantes, comparado con la participación habitual del grupo: cuantos más votantes, más baja, hasta la mayoría M cuando vota todo el mundo.\n\n`Proporción exigida = M + (1 − M) × (1 − (T'/W)^G)`, donde T' es el número de votantes, o el quórum si faltan.\n\n**Los cinco niveles**: el quórum en Monnaie libre, la proporción exigida cuando el grupo vota como de costumbre, luego cuando vota todo el mundo, y lo que está en ese nivel al inicio.\n\n- **Nivel 1 · muy baja**: quórum 12; 60 % como de costumbre, 50 % cuando vota todo el mundo. Ningún artículo al inicio.\n- **Nivel 2 · baja**: quórum 12; 67 % como de costumbre, 50 % cuando vota todo el mundo. Razón de ser, Unicidad de la cuenta, Reciprocidad, y el orden de los artículos de cada sección.\n- **Nivel 3 · media**: quórum 50; 75 % como de costumbre, 55 % cuando vota todo el mundo. El nivel por defecto: todos los demás artículos, y todo artículo nuevo.\n- **Nivel 4 · alta**: quórum 50; 83 % como de costumbre, 60 % cuando vota todo el mundo. El título del documento.\n- **Nivel 5 · muy alta**: quórum 50; 90 % como de costumbre, 66 % cuando vota todo el mundo. Ningún artículo al inicio.\n\n**La participación habitual** es la mediana del número de votantes de las propuestas del grupo votadas en el último año. Hasta que el grupo haya votado 5 propuestas, vale dos veces el quórum de referencia del documento: 100 votantes en Monnaie libre. No depende del quórum de un artículo. Se mantiene entre el 1 % y el 50 % del electorado, y se mide una vez al día.\n\n**G** se calcula para que la proporción exigida, con la participación habitual, sea exactamente la del nivel: `G = ln(1 − (s − M)/(1 − M)) / ln(p̃)`, donde s es la proporción exigida como de costumbre y p̃ la participación habitual dividida por W.\n\nEjemplo: con 7224 miembros y una participación habitual de 120 votantes, cuando votan 120 personas, hacen falta 72, 81, 90, 100 o 108 votos a favor, del nivel 1 al nivel 5; en el nivel 3, el 75 % de los 120 votantes, es decir 90. Por debajo del quórum, los que faltan cuentan como «no»: en el nivel 2 (quórum 12), 9 votos a favor pasan con 3 en contra como mucho; en el nivel 3 (quórum 50), hacen falta 39 votos a favor, con 11 en contra como mucho."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user