Fusion du lot B2 : corrections du vote avant de l'ouvrir (adresse bornée, tâche de fond, plafond, copies non payées, finalité, reçu)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-31 04:02:21 +01:00
co-authored by Claude Opus 5.5
32 changed files with 2534 additions and 181 deletions
+118 -24
View File
@@ -3,7 +3,9 @@
> Complète [0002](0002-vote-permanent.md) (vote permanent) et [0006](0006-acces-connect-trustinwallet.md)
> (accès). Décision d'Yvv du 2026-10-30 (revue, L9) ; le format du bulletin, la voie du
> portefeuille et les réglages sont les choix par défaut d'orch (Q5, Q6, Q7), isolés pour être
> changés sans toucher au reste.
> changés sans toucher au reste. Corrigé le 2026-10-31 après la relecture indépendante de
> `74f96be` (lot B2) : finalité, recherche filtrée et paginée, tâche de fond, plafond, bulletin
> Ğecko, bulletins lâchés, reçu.
**Contexte.** Jusqu'ici, un vote était une ligne écrite par le serveur pour la session connectée.
Dans Monnaie libre, l'électorat W est la toile de confiance, mais la connexion Ğ1
@@ -77,27 +79,53 @@ fait signer par l'extension de portefeuille du navigateur (interface standard `i
l'offrent), avec **polkadot-api chargé au seul moment de signer**, et l'envoie à un nœud Ğ1
(`LD_VOTE_NODES`). Sans portefeuille dans le navigateur : **Ğecko**, par un lien et un QR code
`g1://<compte>?amount=0.1&comment=ld1:<H>` (un virement commenté, ce que Ğecko sait faire) ;
ce bulletin-là n'est lié à aucun compte, et la page attend.
ce bulletin-là n'est lié à aucun compte : seul un commentaire **payé** le tranche (voir la
confirmation), et le serveur le guette, la page ouverte ou non.
La page joint un nœud **avant** de demander la signature (comme polkadot-api, qui lit le nonce
et le runtime) : un nœud muet ne reçoit jamais rien, la page le dit et lâche le bulletin.
### La confirmation — `crates/ld-server/src/ballots.rs`, `indexer.rs`
Le serveur lit l'indexeur (duniter-squid, `LD_INDEXERS`) :
Le serveur lit l'indexeur (duniter-squid, PostGraphile, `LD_INDEXERS`) :
```graphql
query Ballot($hash: String!) {
txComments(condition: {hash: $hash}, orderBy: BLOCK_NUMBER_ASC, first: 20) {
query Ballot($hash: String!, $filter: TxCommentFilter, $after: Cursor) {
squidStatus { height }
txComments(condition: {hash: $hash}, filter: $filter,
orderBy: [BLOCK_NUMBER_ASC, ID_ASC], first: 50, after: $after) {
pageInfo { hasNextPage endCursor }
nodes { id blockNumber remark
author { id identity { index name isMember } }
transfersByCommentId { nodes { fromId toId amount } } } }
}
```
`hash` = `0x` + blake2b-256 des octets de la remarque. Il garde la première occurrence, dans
l'ordre de la chaîne, qui ne vient pas d'un autre compte que celui lié, puis exige :
`hash` = `0x` + blake2b-256 des octets de la remarque. Le `filter` ne demande que ce qui peut
trancher : pour un bulletin lié, `{authorId: {equalTo: <compte lié>}}` ; pour un bulletin Ğecko,
`{transfersByCommentId: {some: {toId: {equalTo: <compte des votes>}, amount:
{greaterThanOrEqualTo: "<montant>"}}}}`. Les commentaires viennent dans l'ordre de la chaîne,
**50 par page, page après page** (au plus 20 pages par lecture), jusqu'à celui qui tranche :
des copies ne masquent jamais le vrai commentaire (l'ancienne requête s'arrêtait à 20 ; la
remarque « Merci » en compte déjà 26 sur la chaîne).
**La finalité.** L'indexeur sert la meilleure chaîne, blocs pas encore définitifs compris.
`squidStatus` n'expose que `name`, `height` et `hash` (pas de `finalizedHeight`) ; `height` est
le dernier bloc définitif traité. Relevé le 2026-10-31 sur indexer.duniter.org, par
`{ squidStatus { height } blocks(orderBy: HEIGHT_DESC, first: 1) { nodes { height } } }` :
`height` reste **3 blocs** derrière le plus récent bloc servi (3 407 340 contre 3 407 343, puis
3 407 342 contre 3 407 345…), les 2 à 3 blocs de la finalité de Duniter v2. Seul un commentaire
d'un bloc ≤ `height` compte, ou refuse ; au-dessus, le bulletin attend.
Le moteur (`ld_engine::ballot::judge`) juge à nouveau ce qui revient — un indexeur qui ignore le
filtre ne change que le nombre de pages. Il garde le premier commentaire, dans l'ordre de la
chaîne, qui n'est pas une copie : pour un bulletin lié, un commentaire d'un autre compte est une
copie ; pour un bulletin Ğecko, **un commentaire sans le virement est une copie** (une remarque
seule ne coûte rien à envoyer). Puis il exige :
1. la remarque exacte ;
2. dans la même transaction, un virement **du signataire vers le compte des votes**, d'au moins
le montant ;
le montant (`transfer` sinon — seulement pour un bulletin lié : son propre compte s'est trompé) ;
3. une identité Ğ1 au signataire (`no_identity` sinon) ;
4. que cette identité soit **membre maintenant** (`not_member` sinon) — par la règle de
trustinWallet.
@@ -107,12 +135,19 @@ avec le bloc et le rang de l'évènement ; le balayage d'adoption tourne aussit
demandé le bulletin voit ces votes comme « ton vote » — et seulement ceux de ses bulletins : une
autre session ne voit jamais ce qu'a voté la même identité ailleurs.
Le serveur relit l'indexeur pour chaque bulletin en attente toutes les 10 secondes (tâche de
fond) et quand la page demande (au plus une lecture toutes les 3 secondes par bulletin). Un
bulletin non vu en **24 heures** est lâché. Cas d'erreur, chacun testé : indexeur muet (le
bulletin reste en attente), signataire non membre ou sans identité (refusé, et dit), mauvais
montant ou mauvais destinataire (refusé), déjà confirmé (rien ne change), révision changée entre
temps (le vote porte sur la révision votée : « à reconfirmer »).
**La tâche de fond** passe toutes les 10 secondes et lit **50 bulletins en attente au plus, le
moins récemment lu d'abord** (jamais lu, puis la plus ancienne lecture) : chaque bulletin en
attente est lu au moins toutes les ⌈n / 50⌉ passes, que la page l'attende ou non (un code Ğecko,
une fenêtre fermée). Il faudrait plus de 430 000 bulletins en attente pour qu'un seul ne soit pas
lu dans ses 24 heures. La page, elle, fait lire son bulletin au plus toutes les 3 secondes.
Un bulletin non vu en **24 heures** est **lu une dernière fois à son échéance**, puis lâché ;
s'aucun indexeur ne répond, ou si son commentaire attend un bloc définitif, il reste en attente
et relu, une heure au plus. Cas d'erreur, chacun testé : indexeur muet (le bulletin reste en
attente), signataire non membre ou sans identité (refusé, et dit), mauvais montant ou mauvais
destinataire (refusé si lié, ignoré si Ğecko), copie nue ou mal payée d'un bulletin Ğecko
(ignorée), bloc pas encore définitif (en attente), déjà confirmé (rien ne change), révision
changée entre temps (le vote porte sur la révision votée : « à reconfirmer »).
### Le dernier fait foi
@@ -122,9 +157,24 @@ un bulletin plus récent sur la chaîne. « Retirer mon vote » est un bulletin
`none`. Les transactions d'un même compte gardent l'ordre de ses envois (le nonce) ; le désordre
ne vient que de l'ordre de lecture de l'indexeur, et la règle l'absorbe.
Un bulletin que le portefeuille n'a pas signé (refus, fonds insuffisants, compte absent) est lâché
aussitôt par la page (`DELETE /api/ballots/{id}` : `expired`, raison `dropped`), pour ne pas
rester « en attente » une journée. Jamais un bulletin confirmé.
### Lâcher ou garder un bulletin
**Rien n'est parti** : la page lâche le bulletin aussitôt (`DELETE /api/ballots/{id}` :
`expired`, raison `dropped`), pour ne pas le montrer « en attente » une journée. C'est le cas :
- d'un refus du portefeuille, d'un compte absent, de fonds insuffisants ;
- d'un nœud muet **avant** la signature (la page joint le nœud d'abord) ;
- d'une fenêtre fermée avant la signature ; si le portefeuille demande encore à ce moment (sa
propre fenêtre peut rester ouverte), le bulletin attend sa réponse : refusé, il est lâché ;
- d'un refus de la chaîne : rejet du nœud (transaction invalide), ou transaction incluse mais
défaite (le virement a échoué, `batchAll` défait tout : aucune remarque).
**La transaction a pu partir** — signée, puis le nœud ou le portefeuille se tait — : le bulletin
est gardé, et la tâche de fond le guette jusqu'à son échéance. Un **code Ğecko** montré est gardé
aussi, fenêtre fermée : on peut payer plus tard, depuis le téléphone. Jamais un bulletin confirmé
n'est lâché.
« Ton vote », sous l'article : un bulletin en attente demandé **avant** un vote confirmé sur la
même ligne ne s'y montre plus ; un bulletin demandé après, si.
### Le vote groupé
@@ -132,7 +182,14 @@ Un panier : article par article ; « tout pour » / « tout contre » pour une s
document — jamais pour un article dont plusieurs versions sont en concurrence : l'interface le
montre, on choisit sa version. Puis **un geste** : une écriture (groupe géré), ou **une
transaction** (chaîne) pour toutes les lignes, au même prix qu'une seule (0,1 Ğ1 par transaction,
pas par ligne ; 200 lignes au plus, 20 bulletins en attente par session).
pas par ligne ; 200 lignes au plus).
**Le plafond vaut par identité, quelle que soit la session** : 20 bulletins au plus à la fois,
en attente **ou lâchés depuis moins d'une heure** — lâcher un bulletin ne libère pas sa place
aussitôt (la relecture avait créé 100 bulletins et 3 500 lignes en une session, par `DELETE`).
La tâche de fond **purge** les bulletins lâchés, lignes comprises : un jour après qu'un
portefeuille ne les a pas signés, une semaine après qu'ils n'ont pas été vus à temps (sous
l'article, il le dit d'ici là). Un bulletin lâché n'a jamais compté : rien d'un vote ne se perd.
### Ce que voit la chaîne
@@ -140,13 +197,37 @@ Qu'un compte (et son identité) a envoyé 0,1 Ğ1 au compte des votes avec une r
tel bloc : **qui a voté et quand, jamais quoi** — ni le document, ni le nombre de lignes, ni les
choix. Le solde du compte des votes est public : il dit combien de votes ont été envoyés.
Recoupée avec les totaux affichés en direct, la chaîne peut laisser deviner un choix (un total
qui bouge juste après un vote) : une décision d'Yvv attend (secret par paliers, ou totaux en
direct). En attendant, les totaux affichés passent par une seule fonction,
`DocState::shown_tally` (et `shown_evaluation`), où une publication par paliers se branchera ;
l'adoption lit `tally`, et les textes publiés (`publish.rs`) suivraient `shown_tally`.
### Ce que sait le serveur
Les bulletins, leurs lignes, leurs sels, la session qui les a demandés et l'identité qui les a
signés : il sait qui a voté quoi, comme pour un vote direct, et ne montre que les totaux. Il ne
peut pas inventer un vote d'une identité (il faudrait sa signature sur la chaîne) ; il pourrait en
taire un. Chaque vote compté se vérifie contre la chaîne en recalculant H depuis les lignes et le
sel — ce que personne d'autre ne peut faire tant que les sels restent au serveur.
sel.
### Le reçu
La note de recherche promet que chacun peut vérifier son propre vote avec son sel. Le serveur
rend donc le bulletin **à l'identité qui l'a demandé, et à elle seule** (404 pour toute autre) :
- `GET /api/ballots/{id}/receipt` — le reçu, à télécharger (`recu-vote-<date>-<id>.json`) : le
document, le compte lié, les lignes (version, révision, choix, et pour lire : l'article et son
titre), le sel, la forme canonique, H, la remarque, son empreinte pour l'indexeur, et où en est
le bulletin (état, bloc, évènement, signataire, identité comptée) ;
- `GET /api/ballots` — son historique, le plus récent d'abord (sans les bulletins jamais signés).
L'interface les montre dans la fenêtre de vote (« Télécharger ton reçu », « Tes bulletins ») et
sur la page **« Tes bulletins »** (`/bulletins`), qui **vérifie un reçu dans le navigateur**, sans
rien demander au serveur : elle recalcule la forme canonique et H (`web/src/lib/receipt.ts`, mêmes
vecteurs que le moteur) et dit si le reçu donne bien sa remarque — à chercher dans l'historique
de son portefeuille, ou sur la chaîne au bloc indiqué. Garder son reçu pour soi : avec le sel,
n'importe qui recalculerait le choix.
### Les réglages (Q7) — `crates/ld-server/src/config.rs`
@@ -166,7 +247,9 @@ Migration `0006`, additive : `ballots` (le bulletin, son sel, H, `0x`+blake2b-25
le compte et le montant du moment, l'état `pending | confirmed | refused | expired`, la raison,
l'identité qui a voté, le signataire, le bloc, le rang, l'évènement, ce qui est entré en vigueur,
les dates) et `ballot_lines` (version, révision, choix). `votes` gagne `source` (`server` par
défaut : tous les votes d'avant restent tels quels), `block`, `event_index`, `ballot_id`.
défaut : tous les votes d'avant restent tels quels), `block`, `event_index`, `ballot_id`. Les
corrections du 2026-10-31 n'ajoutent pas de migration ; la purge efface les bulletins lâchés
(`expired`) passé leur délai, jamais un bulletin confirmé ni refusé, jamais un vote.
## Coût
@@ -178,10 +261,21 @@ défaut : tous les votes d'avant restent tels quels), `block`, `event_index`, `b
indexeurs se configurent ; lire les évènements sur un nœud serait l'étape suivante.
- Le droit de vote se vérifie **au moment de la confirmation sur la chaîne** : un membre sorti
de la toile ensuite garde ses voix.
- Par Ğecko, le bulletin n'est lié à aucun compte : un autre compte qui recopierait la remarque
et la ferait inclure avant (en payant 0,1 Ğ1) prendrait ce bulletin — ses lignes compteraient
pour lui. Par l'extension, le bulletin est lié au compte et la copie est ignorée.
- Un paiement Ğecko fait après 24 heures ne compte pas (il est perdu pour le vote).
- Par Ğecko, le bulletin n'est lié à aucun compte. **Une copie nue de la remarque** (sans
virement, gratuite) **est ignorée** : elle ne refuse plus le bulletin. Seul un commentaire payé
le tranche. **Reste possible, la variante « prise par une copie payée »** : un autre compte qui
voit la remarque (dans la file des transactions, ou sur l'écran) et fait inclure avant une
copie **payée** (0,1 Ğ1 vers le compte des votes) prend ce bulletin — ses lignes comptent pour
lui, le paiement de la personne arrive après et ne compte plus. Elle coûte 0,1 Ğ1 à qui la tente
et se voit sur la chaîne ; un compte sans identité qui paie la copie fait refuser le bulletin
(`no_identity`). Par l'extension, le bulletin est lié au compte et toute copie est ignorée.
- Par Ğecko, un virement d'un mauvais montant ou vers un autre compte laisse le bulletin en
attente (au lieu d'un refus), jusqu'à son échéance : on ne distingue pas une erreur d'une copie.
- Un bulletin est lu une dernière fois à son échéance (24 heures) : ce qui est alors sur la
chaîne, en bloc définitif, compte encore ; un paiement Ğecko plus tardif est perdu pour le vote.
- Une identité Ğ1 créée par un vote sur la chaîne, jamais connectée ici, ne retient pas son nom
face à une personne qui arrive par un lien d'équipe (comme tout membre de la toile qui n'est
jamais venu) ; une identité Ğ1 qui s'est connectée le garde.
- polkadot-api : environ 180 Ko (66 Ko compressés) chargés au moment de signer seulement ;
`node_modules` grossit d'environ 77 Mo (son outillage).
- La chaîne montre la participation de chacun (qui a voté, quand, combien de fois).
@@ -229,7 +229,9 @@ du bulletin (`ld1:` + 64 chiffres hexadécimaux). La voix est celle de **l'ident
qui signe**, membre au moment où le serveur confirme la transaction sur l'indexeur ; quelle que
soit la session (invitation ou connexion Ğ1), la même identité n'a qu'une voix par version, et
**le dernier vote fait foi**, dans l'ordre de la chaîne. La chaîne montre qui a voté et quand,
jamais quoi ; chacun ne voit que les totaux. Sans compte des votes configuré
jamais quoi ; chacun ne voit que les totaux. Chacun reçoit **le reçu de son bulletin** (ses votes
et son sel), le retrouve sur « Tes bulletins » et le vérifie dans son navigateur ; personne
d'autre ne le reçoit (ADR 0008, « Le reçu »). Sans compte des votes configuré
(`LD_VOTE_ACCOUNT`), le vote par la chaîne est fermé et l'interface le dit. Dans les groupes
gérés (essai, Axiom…), tout membre vote directement, sans portefeuille.
@@ -259,9 +261,11 @@ Les raisons longues sont dans `docs/adr/`.
de la toile, ou dont l'accès est révoqué, garde les voix déjà confirmées.
- Le serveur fait confiance à l'indexeur pour lire la transaction (remarque, virement, identité,
appartenance à la toile) ; plusieurs indexeurs se configurent (ADR 0008).
- Par Ğecko, le bulletin n'est lié à aucun compte : une copie de son sceau incluse avant lui par
un autre compte le prendrait. Par l'extension, il est lié au compte qui signe.
- Un bulletin non vu sur la chaîne en 24 heures est lâché : un paiement plus tardif ne compte pas.
- Par Ğecko, le bulletin n'est lié à aucun compte : une copie nue de son sceau est ignorée, mais
une copie **payée** (0,1 Ğ1) incluse avant lui par un autre compte le prendrait. Par
l'extension, il est lié au compte qui signe.
- Un bulletin non vu sur la chaîne en 24 heures est lu une dernière fois, puis lâché : un
paiement plus tardif ne compte pas.
- Dans un groupe géré (essai, Axiom…), une même personne admise sous une identité d'invitation
**et** sous sa clé Ğ1 aurait deux voix : c'est à l'administration de n'en admettre qu'une.
- Un administrateur peut créer un nouveau lien pour une identité d'invitation existante
@@ -6,7 +6,7 @@ sur un contrôle vaut échec du contrôle tant qu'elle n'est pas traitée.
Où tester : les contrôles qui écrivent (C5, C6, C7, C21, C22) se font dans le **groupe
d'essai** (caché, plancher C = 1, jamais publié) — jamais sur le texte de la communauté. Les
votes par la chaîne Ğ1 (C11, C17, C23) ne se jouent, pour l'automate, que sur une **chaîne
votes par la chaîne Ğ1 (C11, C17, C23, C32, C33) ne se jouent, pour l'automate, que sur une **chaîne
simulée** (`web/e2e/chain-mock.mjs` : un faux nœud, un faux indexeur, un faux portefeuille) ;
pour une personne, ils coûtent 0,1 Ğ1 réels : c'est un acte « paye » d'Yvv.
@@ -36,7 +36,9 @@ pour une personne, ils coûtent 0,1 Ğ1 réels : c'est un acte « paye » d'Yvv.
| C21 | Le vote déplié | groupe d'essai : Unicité du compte → « Le vote », puis « Pour » ; une autre personne ouvre le même vote | sous l'article : le niveau d'eau, « 0 pour · 0 contre », « Seuil : … voix pour », « Il manque … », « Tu n'as pas voté sur cette version. » ; après « Pour » : « Ton vote : pour · enregistré », « 1 pour · 0 contre », le bouton enfoncé, « Ton vote ne se montre à personne : seuls les totaux sont visibles. » ; l'autre personne voit 1 pour, jamais qui ; « Le vote » replie ; « Retirer mon vote » remet 0 |
| C22 | Le vote groupé | groupe d'essai : « Propositions en cours » → « Engagements techniques » → « Voter cette section » ; « Tout pour » ; dans « Connaissance suffisante », « Pour » sur la variante 1.0.0 ; recharger ; « Voter (n) » | le panier s'ouvre sur la section ; « Tout pour » remplit les articles à une version, pas « Connaissance suffisante » ni « Rencontre physique… » (« Plusieurs versions : choisis la tienne. », et le compte des articles laissés) ; le panier reste après rechargement (« Voter en lot · n ») ; un seul envoi : « n votes enregistrés. », le panier vide, la variante choisie à 1 pour |
| C23 | Le vote par la chaîne Ğ1 | Monnaie libre, un portefeuille dans le navigateur : « Voter en lot », « Pour » sur Unicité du compte et Réciprocité, « Signer et envoyer (2) » ; puis sous Réciprocité, « Contre » (l'indexeur en retard) puis « Retirer mon vote » ; puis sans portefeuille : « Pour » → « Passer par Ğecko » | une seule signature pour les deux : 0,1 Ğ1 vers le compte des votes et une remarque `ld1:` + 64 chiffres, rien du vote en clair ; « Ton vote compte : il est inscrit sur la chaîne Ğ1. », « Compté pour l'identité Ğ1 « Alice ». » ; sous l'article « Ton vote : pour · inscrit sur la chaîne Ğ1 », 1 pour ; « En attente de la chaîne Ğ1 : contre. » ; le retrait, plus récent sur la chaîne, fait foi même quand le « contre » est confirmé après lui (0 pour · 0 contre) ; Ğecko : un QR code et le lien `g1://<compte>?amount=0.1&comment=ld1:…`, puis la confirmation dès que la chaîne la montre |
| C24 | Sans compte des votes | Monnaie libre sur un serveur sans `LD_VOTE_ACCOUNT`, puis le groupe d'essai | pas de « Voter en lot » ni « Voter cette section » ; sous l'article : « Le vote par la chaîne Ğ1 n'est pas encore ouvert : le compte qui reçoit les votes reste à créer. Tu peux lire, proposer et signaler une contradiction. », pas de bouton, les totaux et le seuil restent ; dans le groupe d'essai, le panier et les boutons sont là |
| C24 | Sans compte des votes | Monnaie libre sur un serveur sans `LD_VOTE_ACCOUNT`, puis le groupe d'essai | pas de « Voter en lot » ni « Voter cette section » ; sous l'article : « Le vote par la chaîne Ğ1 n'est pas encore ouvert : le compte qui reçoit les votes reste à créer. Tu peux lire, proposer et signaler une contradiction. », pas de bouton, les totaux et le seuil restent ; le panier ouvert par son adresse (`?onglet=panier`) dit la même chose, son bouton éteint, et **jamais** « Tes votes comptent dès l'envoi » ; dans le groupe d'essai, le panier et les boutons sont là |
| C32 | Lâcher ou garder un bulletin | Monnaie libre, un portefeuille dans le navigateur (Eve, chaîne simulée), « Propositions en cours » → Document de révocation : « Contre » le nœud éteint ; puis « Contre », fermer la fenêtre pendant que le portefeuille demande, refuser dans le portefeuille ; puis « Contre », fermer, signer dans le portefeuille ; une autre personne, sans portefeuille : « Contre » → « Passer par Ğecko », fermer, payer depuis un téléphone | nœud éteint : « La chaîne Ğ1 ne répond pas. Réessaie dans un moment. », le bulletin lâché aussitôt, rien « en attente » sous l'article ; fenêtre fermée puis refus : lâché ; fenêtre fermée puis signature : gardé, et la chaîne le confirme sans la page (« Ton vote : contre · inscrit sur la chaîne Ğ1 ») ; Ğecko, fenêtre fermée : « En attente de la chaîne Ğ1 : contre. », puis confirmé sans la page |
| C33 | Le reçu de ton vote | Monnaie libre, un portefeuille : « Contre » sur Ne jamais certifier seul (« Propositions en cours »), « Télécharger ton reçu », « Tes bulletins » ; sur « Tes bulletins », « Fichier du reçu » : le reçu ; puis coller le reçu avec un choix changé, « Vérifier » ; une autre personne ouvre « Tes bulletins » | le reçu `recu-vote-<date>-<id>.json` : les votes, le sel, la remarque `ld1:…`, « confirmed », l'identité « Alice » ; « Tes bulletins de vote » : « Compté pour l'identité Ğ1 « Alice » » et la remarque ; « Le reçu tient : ses votes et son sel donnent bien ce commentaire. », « Ne jamais certifier seul : contre » ; changé : « Le reçu ne tient pas » ; l'autre personne : « Tu n'as encore envoyé aucun bulletin par la chaîne Ğ1. », et le reçu lui est refusé |
## Au-delà de la liste
@@ -66,3 +68,4 @@ page, la langue et la taille de l'écran. Les retours se traitent dans trustinWa
| 2026-10-30 | automate | local, lot A (entrée, logo, ambiances, menu, listes, Décisions) | C1–C17, M1 | 18 / 18 verts |
| 2026-10-30 | automate | local, lot C (inertie à cinq crans, réglages de la formule) | C1–C20, M1 | 21 / 21 verts |
| 2026-10-31 | automate | local, lot B (le vote déplié, le panier, le vote par la chaîne Ğ1 simulée) | C1–C24, M1 | 25 / 25 verts |
| 2026-10-31 | automate | local, lot B2 (vote par la chaîne : corrections de la relecture de `74f96be`, reçu), `c7d2791` | C1–C24, C32, C33, M1 | 27 / 27 verts |