|
|
|
@@ -0,0 +1,296 @@
|
|
|
|
|
# 0012 — Le tampon des votes : l'intention tout de suite, la preuve de la chaîne, la réconciliation
|
|
|
|
|
|
|
|
|
|
> Complète [0008](0008-vote-par-transaction.md) (vote par la chaîne Ğ1) et révise une phrase de
|
|
|
|
|
> [0010](0010-objections-et-coquilles-validees.md) (un bulletin scellé avant une objection et
|
|
|
|
|
> confirmé après). Demande d'Yvv du 2026-11-02. **Deux décisions d'Yvv font foi** : le poids
|
|
|
|
|
> d'un vote simulé, « Il compte, puis s'efface » (2026-11-02, 06 h 26), et la publication d'un
|
|
|
|
|
> texte entré par des votes simulés, « Pas avant des votes prouvés » (2026-11-03, formulaire d'orch). La
|
|
|
|
|
> façon de compter une intention et ce qu'on dit quand elle est écartée restent les choix du lot
|
|
|
|
|
> V, isolés dans `ld_engine::buffer` pour être changés seuls. Se lit avec
|
|
|
|
|
> [0013](0013-quorum.md) (le quorum, jugé sur les totaux montrés). Lot V, 2026-11-02 et 03.
|
|
|
|
|
|
|
|
|
|
**Contexte.** Yvv, 2026-11-02 : « Simule le vote à tout endroit. […] du point de vue de
|
|
|
|
|
l'architecture, il faut prévoir le "tampon" entre la base des votes et la mise à jour par les
|
|
|
|
|
indexeurs de la chaîne. Il faut prévoir le bon process pour une ux moderne "je vois ce que j'ai
|
|
|
|
|
fait sans attendre", avec un contrôle a posteriori de l'intégrité du vote (bien unique, bien le
|
|
|
|
|
dernier qui vaut). » Et ses décisions déjà prises : une personne, une voix, autant de fois
|
|
|
|
|
qu'elle veut, le dernier vote fait foi ; dans Monnaie libre l'intégrité vient de la chaîne Ğ1
|
|
|
|
|
(0,1 Ğ1 par vote, un seul geste, un bulletin scellé) ; les totaux se voient en direct et
|
|
|
|
|
**chacun voit son propre vote en direct** (« tout le monde a maintenant cette habitude,
|
|
|
|
|
difficile de régresser ») ; rien n'est dit à l'écran du recoupement entre la chaîne et les
|
|
|
|
|
totaux ; sur une version retenue par une objection, « contre » et retrait, jamais « pour ».
|
|
|
|
|
|
|
|
|
|
Jusqu'ici, dans Monnaie libre, un bulletin restait « en attente » sans rien compter jusqu'à ce
|
|
|
|
|
que l'indexeur le confirme (6 à 30 secondes, une journée au plus par Ğecko), et sans
|
|
|
|
|
`LD_VOTE_ACCOUNT` le vote était éteint (« pas encore ouvert »). Un bulletin scellé avant une
|
|
|
|
|
objection et payé après comptait « pour » (relevé par une relecture).
|
|
|
|
|
|
|
|
|
|
## Décision
|
|
|
|
|
|
|
|
|
|
### Le cheminement d'un vote : trois temps
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
geste ──► INTENTION ──────────► PREUVE ─────────────► RÉCONCILIATION ──► votes (ce qui compte)
|
|
|
|
|
ballots (pending) la chaîne, lue par ld_engine::buffer
|
|
|
|
|
visible aussitôt l'indexeur ; ou le ::reconcile, par
|
|
|
|
|
(ton vote, totaux) confirmateur simulé identité et version
|
|
|
|
|
│
|
|
|
|
|
└── jamais prouvée dans sa fenêtre, refusée par la chaîne,
|
|
|
|
|
dépassée, « pour » prouvé sur une version retenue : ÉCARTÉE
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
1. **L'intention** est le geste lui-même : un bulletin scellé (`ballots`, `ballot_lines`,
|
|
|
|
|
0008), un seul pour tout un panier. Elle est écrite **avant** que le portefeuille signe et se
|
|
|
|
|
voit **aussitôt** : pour la personne (« ton vote », état `pending`) et dans les totaux
|
|
|
|
|
montrés (`DocState::shown_tally`). Rien n'est parti (refus du portefeuille, nœud muet,
|
|
|
|
|
fenêtre fermée avant la signature) : la page la lâche aussitôt (`DELETE`, 0008) et elle
|
|
|
|
|
disparaît des totaux. Un code Ğecko montré est gardé, comme avant.
|
|
|
|
|
2. **La preuve** vient de la chaîne, lue par l'indexeur, sans rien changer à 0008 : la
|
|
|
|
|
remarque exacte, le virement du signataire vers le compte des votes, une identité Ğ1 membre
|
|
|
|
|
au moment de la confirmation, un bloc définitif. Pendant la simulation (plus bas), un
|
|
|
|
|
**confirmateur simulé** tient la place de l'indexeur.
|
|
|
|
|
3. **La réconciliation** a lieu à chaque preuve, pour chaque version du bulletin, et pour
|
|
|
|
|
l'identité qui a signé : elle relit **toutes** les lignes prouvées de cette identité sur
|
|
|
|
|
cette version et en tire la voix qui tient (`ld_engine::buffer::reconcile`, pur, testé) :
|
|
|
|
|
- **une seule voix par identité Ğ1** et par version ;
|
|
|
|
|
- **la dernière dans l'ordre de la chaîne** (le bloc, puis le rang de l'évènement), quel que
|
|
|
|
|
soit l'ordre dans lequel l'indexeur les a lues : le résultat ne dépend pas de l'ordre des
|
|
|
|
|
confirmations ; « retirer mon vote » (`none`) qui tient efface la voix ;
|
|
|
|
|
- elle **écarte** : ce que la chaîne ne confirme jamais dans la fenêtre de validité (24
|
|
|
|
|
heures, plus une heure de grâce si l'indexeur se tait ou si le bloc n'est pas encore
|
|
|
|
|
définitif : `expired`) ; ce que la chaîne refuse (`refused` : pas d'identité, pas membre,
|
|
|
|
|
virement manquant) ; ce qu'un vote plus récent de la même identité a dépassé
|
|
|
|
|
(`superseded`) ; et **un « pour » dont la preuve arrive pendant qu'une objection retient la
|
|
|
|
|
version** (`objection`, voir plus bas).
|
|
|
|
|
|
|
|
|
|
Chaque ligne d'un bulletin prouvé porte son issue (`ballot_lines.outcome` : `counted`,
|
|
|
|
|
`superseded`, `objection`), recalculée à chaque réconciliation sauf `objection`, fixée au
|
|
|
|
|
moment de la preuve. La table `votes` reste ce qui compte : une ligne par identité et par
|
|
|
|
|
version, écrite par la réconciliation (sources `chain`, `simulated`) ou par le serveur dans
|
|
|
|
|
un groupe géré (`server`). Chaque vote compté se vérifie toujours contre la chaîne en
|
|
|
|
|
recalculant H depuis les lignes et le sel (0008).
|
|
|
|
|
|
|
|
|
|
**Dans un groupe géré** (essai, Axiom…), il n'y a pas de tampon : le serveur est l'autorité, le
|
|
|
|
|
vote est écrit d'un coup et il est enregistré (`recorded`) dès l'écriture, comme avant.
|
|
|
|
|
|
|
|
|
|
### Les états d'un vote, et ce que chacun pèse
|
|
|
|
|
|
|
|
|
|
| État | Ce que c'est | `myVoteState` | Totaux montrés (`shown_tally`) | Adoption (`tally`) |
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
| enregistré | prouvé par la chaîne, ou vote du serveur d'un groupe géré | `recorded` | oui | oui |
|
|
|
|
|
| en attente | une intention sans preuve, dans sa fenêtre | `pending` | oui, **à la place** du vote que la personne voit comme le sien ; jamais un « pour » sur une version retenue | **jamais** |
|
|
|
|
|
| simulé | un vote de la simulation, chaîne fermée | `simulated` | oui, **jusqu'à l'acte « supprime »** | oui, **jusqu'à l'acte « supprime »** (décision d'Yvv, « Il compte, puis s'efface ») |
|
|
|
|
|
| écarté | expiré, refusé, dépassé, « pour » sur une version retenue | — | non | non |
|
|
|
|
|
|
|
|
|
|
- **L'adoption ne compte jamais une intention non confirmée.** Une entrée en vigueur ne repose
|
|
|
|
|
que sur des votes prouvés, sur les votes du serveur dans un groupe géré, et sur les votes
|
|
|
|
|
simulés jusqu'à l'acte qui les écarte (plus bas). Le balayage d'adoption et la mesure de la
|
|
|
|
|
participation p̃ lisent les mêmes poids (`ld_engine::buffer::Rules`).
|
|
|
|
|
- **Le quorum se juge sur les totaux montrés** (ADR 0013) : l'évaluation qu'on montre
|
|
|
|
|
(`evaluation` : `quorum`, `yesNeeded`, `noNeeded`, `missing`) et les cinq crans du détail se
|
|
|
|
|
calculent sur `shown_tally` — votes enregistrés, en attente et simulés, l'intention à la place
|
|
|
|
|
du vote vu de la personne (`DocState::shown_evaluation`) ; l'adoption, sur `tally`
|
|
|
|
|
(`DocState::evaluation`).
|
|
|
|
|
- **Une intention compte pour la session qui l'a demandée** : elle remplace, dans les totaux
|
|
|
|
|
montrés, le vote que cette session voit comme le sien (`my_votes`), et la dernière intention
|
|
|
|
|
de la session sur une version remplace les précédentes. Une intention sur une révision
|
|
|
|
|
dépassée ne compte pas (elle serait « à reconfirmer ») mais retire bien le vote d'avant ; un
|
|
|
|
|
« pour » en attente sur une version retenue ne pèse rien tant qu'elle est retenue (il serait
|
|
|
|
|
écarté) : `ld_engine::buffer::weigh_intent`.
|
|
|
|
|
- `tallyPending` dit la part des totaux montrés qui attend encore sa preuve ; `tallySimulated`
|
|
|
|
|
la part qui vient de la simulation. Les textes publiés (`publish.rs`) ne publient jamais une
|
|
|
|
|
intention : ils lisent `recorded_tally` (ce qui est enregistré, simulation comprise tant
|
|
|
|
|
qu'elle compte), ou `tally` pour les articles en vigueur, comme avant ; et rien ne part tant
|
|
|
|
|
qu'une entrée due à des votes simulés ne tient pas sur les seuls votes prouvés (« La
|
|
|
|
|
publication », plus bas).
|
|
|
|
|
|
|
|
|
|
### Ce que la personne voit, à chaque étape
|
|
|
|
|
|
|
|
|
|
| Étape | Sous l'article (`myVote`, `myVoteState`, `myBallot`) | Totaux | Boîte « Messages » |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| elle vote (groupe géré) | son choix, `recorded` | aussitôt | — |
|
|
|
|
|
| elle vote, la chaîne ouverte | son choix, `pending` ; « En attente de la chaîne Ğ1 : … » | aussitôt, avec son choix | — |
|
|
|
|
|
| la chaîne le confirme | son choix, `recorded` ; « inscrit sur la chaîne Ğ1 » | inchangés (l'intention devient preuve) | — |
|
|
|
|
|
| rien n'est parti (portefeuille, nœud, fenêtre fermée) | son vote d'avant | son intention retirée | — |
|
|
|
|
|
| jamais vu sur la chaîne en 24 h | son vote d'avant ; « Ton dernier vote n'a pas été vu sur la chaîne Ğ1 à temps » | son intention retirée | « Ton vote … n'a pas été vu sur la chaîne Ğ1 en 24 heures : il est écarté. Tu peux voter à nouveau. » |
|
|
|
|
|
| refusé par la chaîne | son vote d'avant ; « Ton dernier vote n'a pas été compté : … » | son intention retirée | « Ton vote … n'a pas été compté : … » |
|
|
|
|
|
| un « pour » prouvé sur une version retenue | son vote d'avant ; « Ton dernier vote n'a pas été compté : une objection retenait cette version… » | inchangés | « Ton « pour » sur … est écarté : une objection retenait cette version quand la chaîne a confirmé ton vote. Ton vote d'avant reste. » |
|
|
|
|
|
| dépassé par un vote plus récent de la même identité | le plus récent | le plus récent | — (c'est son propre geste) |
|
|
|
|
|
| elle vote, la chaîne fermée (simulation) | son choix, `simulated` | aussitôt | — |
|
|
|
|
|
|
|
|
|
|
Rien n'est dit à l'écran sur le recoupement entre la chaîne et les totaux (décision d'Yvv).
|
|
|
|
|
Un message n'offre aucun geste : il dit ce qui s'est passé et mène au texte, où l'on revote.
|
|
|
|
|
|
|
|
|
|
### La simulation, tant que la chaîne est fermée
|
|
|
|
|
|
|
|
|
|
Sans `LD_VOTE_ACCOUNT`, on vote **quand même, partout, dans Monnaie libre**, sans portefeuille :
|
|
|
|
|
le bouton de vote s'affiche (l'accès dit `open: true`, `simulated: true`, `way: "server"`), le
|
|
|
|
|
vote part par l'écriture du serveur (`POST /api/proposals/{id}/vote`, `POST
|
|
|
|
|
/api/docs/{id}/votes`) et passe **par le même tampon** : un bulletin scellé (`ballots.mode =
|
|
|
|
|
'simulated'`, son sel et son empreinte, sans compte ni montant), puis le **confirmateur simulé**
|
|
|
|
|
le prouve aussitôt, dans la même requête (et le balayage reprend un bulletin simulé resté en
|
|
|
|
|
attente) : la voix est celle de **l'identité de la session** (il n'y a pas de portefeuille), sa
|
|
|
|
|
position est « bloc 0 », au rang de son arrivée ; puis la même réconciliation, le même
|
|
|
|
|
balayage d'adoption, les mêmes messages. L'architecture est ainsi éprouvée de bout en bout, en
|
|
|
|
|
production, sans la chaîne. Qui peut voter en simulation : la règle de trustinWallet,
|
|
|
|
|
`trustin_access::casts_simulated` (qui ouvre le groupe, la chaîne fermée) — une personne
|
|
|
|
|
invitée comprise.
|
|
|
|
|
|
|
|
|
|
**La décision d'Yvv, 2026-11-02 à 06 h 26 : « Il compte, puis s'efface ».**
|
|
|
|
|
|
|
|
|
|
- **Il compte.** Un vote simulé compte dans les totaux et **pour l'entrée en vigueur**, comme un
|
|
|
|
|
vote prouvé, avec la marque « simulé » (`myVoteState`, `tallySimulated`, `mode` du bulletin,
|
|
|
|
|
« Tes bulletins »). Avec les quorums de l'ADR 0013, neuf « pour » simulés suffisent à faire
|
|
|
|
|
entrer I1, E1 ou E2 au cran 2 (quorum 12) : le texte est alors en vigueur sur le site, et ne
|
|
|
|
|
se publie pas (« La publication », plus bas). La règle : `ld_engine::buffer::SIMULATED_ADOPTS
|
|
|
|
|
= true`, reprise par `Config::simulated_adopts` ; à `false`, « simulé = affiché, jamais compté
|
|
|
|
|
pour l'adoption » (les totaux montrés ne changent pas, l'adoption et p̃ les ignorent). Les deux
|
|
|
|
|
branches sont testées.
|
|
|
|
|
- **L'ouverture de la chaîne n'efface rien d'elle-même.** Dès que `LD_VOTE_ACCOUNT` est posé
|
|
|
|
|
(un acte « déploie »), on vote par la chaîne : un nouveau vote simulé est refusé (`409
|
|
|
|
|
chain_only`). Les votes simulés déjà là **continuent de compter**, marqués, jusqu'à l'acte qui
|
|
|
|
|
suit. Une identité Ğ1 (une session ouverte par la clé Ğ1, `idty:<index>`) qui vote ensuite
|
|
|
|
|
par la chaîne passe devant son vote simulé (bloc 0, avant tout bloc réel).
|
|
|
|
|
- **Puis il s'efface, par un acte « supprime » d'Yvv**, jamais déclenché par le code : ni le
|
|
|
|
|
serveur, ni l'ouverture de la chaîne, ni un balayage ne l'appellent. Il est préparé et testé
|
|
|
|
|
ici : `simulation::discard`, en ligne de commande dans l'image :
|
|
|
|
|
```sh
|
|
|
|
|
ld-server discard-simulation # à blanc : dit ce qu'il ferait, n'écrit rien
|
|
|
|
|
ld-server discard-simulation --apply # l'acte
|
|
|
|
|
```
|
|
|
|
|
Il lit la base du volume (`LD_DATA_DIR`, `libredecision.db`) et imprime un JSON : les
|
|
|
|
|
bulletins, lignes, votes et messages simulés, et chaque entrée réexaminée (article, sorte,
|
|
|
|
|
votes d'entrée et leur part simulée, votes prouvés, « pour » exigés, tient ou non, rendue ou
|
|
|
|
|
non, la version déplacée). Avec `--apply` :
|
|
|
|
|
1. **le tri** : il efface les messages des bulletins simulés, les votes simulés
|
|
|
|
|
(`votes.source = 'simulated'`), leurs lignes et les bulletins simulés, puis réconcilie de
|
|
|
|
|
nouveau chaque identité qui avait voté en simulation (son vote prouvé, s'il y en a un,
|
|
|
|
|
tient) ;
|
|
|
|
|
2. **le réexamen** : chaque entrée en vigueur qui a compté des votes simulés
|
|
|
|
|
(`adoptions.params.simulated`), la plus récente d'abord, encore en vigueur, est jugée de
|
|
|
|
|
nouveau sur les seuls votes prouvés, au cran et sur le quorum où elle a été votée
|
|
|
|
|
(`params.level`, `params.quorum`). **Sous son seuil, elle retourne parmi les
|
|
|
|
|
propositions** : la version qu'elle avait déplacée revient en vigueur (un texte retiré
|
|
|
|
|
revient, un cran d'inertie redescend, l'ordre et le titre d'avant s'appliquent de
|
|
|
|
|
nouveau), son adoption est effacée, une publication est mise en file. **À son seuil ou
|
|
|
|
|
au-dessus, elle reste**, et son adoption le dit (`params.reexamined`) ;
|
|
|
|
|
3. les envois en attente de la publication repartent.
|
|
|
|
|
|
|
|
|
|
Répété, l'acte ne trouve plus rien (aucun bulletin simulé, aucune entrée à réexaminer).
|
|
|
|
|
**À blanc**, le réexamen met les votes simulés de côté sans les effacer : une entrée qui ne
|
|
|
|
|
reviendrait qu'après le retour d'une plus récente est dite « plus en vigueur ».
|
|
|
|
|
- Pour les tests seulement, et seulement avec la connexion de développement, `POST
|
|
|
|
|
/api/dev/vote-chain {"open": false}` ferme la chaîne d'un serveur qui en a une (le contrôle
|
|
|
|
|
e2e de la simulation tourne sur le serveur jetable des e2e, qui a sa chaîne simulée).
|
|
|
|
|
|
|
|
|
|
### La publication : « Pas avant des votes prouvés »
|
|
|
|
|
|
|
|
|
|
**La décision d'Yvv, 2026-11-03 (formulaire d'orch) : « Pas avant des votes prouvés ».** Un texte qui
|
|
|
|
|
entre en vigueur grâce à des votes simulés est **en vigueur sur le site**, mais **ne déclenche
|
|
|
|
|
pas la publication** vers le dépôt des textes, que lisent les applis clientes. La publication
|
|
|
|
|
d'un document attend que tout ce qui y est en vigueur et y est entré par des votes simulés —
|
|
|
|
|
texte, retrait, cran d'inertie, ordre, titre — **tienne sur les seuls votes prouvés**, selon le
|
|
|
|
|
réexamen même de l'acte « supprime », fait à blanc (`simulation::unproven`). La chaîne étant
|
|
|
|
|
fermée, **rien ne se publie dans Monnaie libre avant son ouverture** ; après elle, la file part
|
|
|
|
|
dès que l'acte a rendu ce qui ne tient pas, ou dès que des votes prouvés portent ce qui est
|
|
|
|
|
entré.
|
|
|
|
|
|
|
|
|
|
- Un seul endroit : `publish::waits_for_proven_votes`, après `publish::waits_for_a_text` (ADR
|
|
|
|
|
0011), tous deux lus par `publish::waits`. Comme pour l'attente du premier texte, ce n'est pas
|
|
|
|
|
un échec : aucune tentative comptée, la file attend et se relit toutes les dix minutes ; l'API
|
|
|
|
|
dit `publication.state = "suspended"` et `publication.waitsFor = "proven"` (`"text"` pour
|
|
|
|
|
l'attente du premier texte).
|
|
|
|
|
- La bascule, pour publier quand même si Yvv revenait sur ce choix : `LD_PUBLISH_SIMULATED=1`
|
|
|
|
|
(`Publish::simulated`). Testé dans les deux sens.
|
|
|
|
|
- Plus strict qu'« un texte prouvé suffit » : tant qu'une entrée simulée ne tient pas, même un
|
|
|
|
|
texte prouvé du même document attend, pour que le dépôt ne porte jamais une entrée que les
|
|
|
|
|
votes prouvés ne portent pas.
|
|
|
|
|
|
|
|
|
|
### Un « pour » prouvé pendant une objection (révise 0010)
|
|
|
|
|
|
|
|
|
|
0010 disait : « un bulletin scellé avant l'objection et confirmé après compte tel qu'il est
|
|
|
|
|
signé, sans la faire entrer ». Une relecture a relevé qu'un « pour » entrait ainsi sur une
|
|
|
|
|
version qui ne doit plus en prendre. Désormais, **un « pour » dont la preuve arrive pendant
|
|
|
|
|
qu'une objection retient sa version est écarté** (`objection`) : il ne compte pas, comme s'il
|
|
|
|
|
n'avait pas été émis ; le vote d'avant de la même identité, s'il y en a un, reste ; la personne
|
|
|
|
|
qui l'a demandé reçoit un message. « Contre » et retrait, prouvés pendant l'objection, comptent
|
|
|
|
|
(Yvv, 01/11 : « Retirer ou "contre" »). « Arrive » se lit à l'heure du serveur qui lit la
|
|
|
|
|
preuve : la chaîne ne donne que des blocs, et l'objection une heure. Une objection levée avant
|
|
|
|
|
la preuve laisse le « pour » compter.
|
|
|
|
|
|
|
|
|
|
### « Un vote existe » (ADR 0013)
|
|
|
|
|
|
|
|
|
|
Le quorum d'un article se fixe avant le premier vote (`quorum::voted_units`, la seule
|
|
|
|
|
requête). Le tampon gardant l'historique des bulletins, elle compte : un vote qui compte
|
|
|
|
|
(serveur, chaîne, **simulé**) ; toute ligne d'un bulletin **en attente** ou **prouvé**, de la
|
|
|
|
|
chaîne ou de la simulation, **quel que soit son sort** (la voix qui compte, un vote dépassé, un
|
|
|
|
|
« pour » écarté par une objection, **un retrait**) ; une adoption. Un vote retiré sur la chaîne
|
|
|
|
|
ou en simulation laisse donc sa trace. Un bulletin lâché sans signature, jamais vu à temps ou
|
|
|
|
|
refusé par la chaîne n'est pas un vote. Deux limites : dans un groupe géré, sans tampon, un vote
|
|
|
|
|
retiré avant toute adoption ne laisse rien (ADR 0013) ; l'acte « supprime » emporte la trace des
|
|
|
|
|
votes simulés avec eux.
|
|
|
|
|
|
|
|
|
|
### L'API, contrat partagé avec les lots U et S
|
|
|
|
|
|
|
|
|
|
Dans le JSON d'une proposition (`GET /api/docs/{id}`, `GET /api/proposals/{id}`) :
|
|
|
|
|
|
|
|
|
|
- `tally: {for, against}` — les totaux montrés, intentions comprises ;
|
|
|
|
|
- `tallyPending: {for, against}` — la part encore sans preuve ;
|
|
|
|
|
- `tallySimulated: {for, against}` — la part simulée (ajouté) ;
|
|
|
|
|
- `evaluation` — sur les totaux montrés (ADR 0013 : `quorum`, `yesNeeded`, `noNeeded`) ;
|
|
|
|
|
- `myVote: 'for' | 'against' | null` — ce que la personne voit comme son vote maintenant : son
|
|
|
|
|
dernier geste, une intention en attente comprise (un retrait en attente : `null`) ;
|
|
|
|
|
- `myVoteState: 'recorded' | 'pending' | 'simulated' | null` ;
|
|
|
|
|
- inchangés : `myVoteStale`, `myVoteChain`, `myBallot` (qui peut dire `status: 'refused'`,
|
|
|
|
|
`reason: 'objection'` pour un « pour » écarté).
|
|
|
|
|
|
|
|
|
|
Dans l'accès (`vote` du JSON du document) : `{ way, open, simulated, reason, chain }` ;
|
|
|
|
|
pendant la simulation, `way: "server"`, `open: true`, `simulated: true`, `chain: null`. Un
|
|
|
|
|
vote écrit par le serveur répond `{ entered, simulated }` (et `count` pour un vote groupé).
|
|
|
|
|
Les bulletins (`GET /api/ballots/{id}`, `GET /api/ballots`, le reçu) gagnent `mode: 'chain' |
|
|
|
|
|
'simulated'` (le bloc d'un bulletin simulé n'est jamais montré). Les messages gagnent le sujet
|
|
|
|
|
`ballot`, de sortes `ballot_expired`, `ballot_refused`, `ballot_objection`, avec `ballot: {id,
|
|
|
|
|
status, reason, mode, createdAt, settledAt, lines: [{proposalId, unitId, code, title, choice,
|
|
|
|
|
outcome}]}`. La publication d'un document (`doc.publication`) gagne `waitsFor: 'text' |
|
|
|
|
|
'proven' | null`.
|
|
|
|
|
|
|
|
|
|
## Les données
|
|
|
|
|
|
|
|
|
|
Migration `0010_vote_buffer.sql`, additive (S a pris `0009` et `0011` ; les trois passent dans
|
|
|
|
|
l'ordre sur une base neuve comme sur une base arrêtée à `0008`, la production) :
|
|
|
|
|
|
|
|
|
|
- `ballots.mode TEXT NOT NULL DEFAULT 'chain'` (`chain` | `simulated`) : tous les bulletins
|
|
|
|
|
d'avant restent des bulletins de la chaîne ;
|
|
|
|
|
- `ballot_lines.outcome TEXT` (`counted` | `superseded` | `objection`, `NULL` tant que le
|
|
|
|
|
bulletin n'est pas prouvé ; les lignes d'avant restent à `NULL` et se lisent comme « pas
|
|
|
|
|
écartée par une objection » : la prochaine réconciliation de leur identité les qualifie) ;
|
|
|
|
|
- un index `ballots(doc_id, status)` pour lire les intentions d'un document ;
|
|
|
|
|
- `votes.source` prend une valeur de plus, `simulated` (colonne texte, rien à changer) ;
|
|
|
|
|
`messages.topic` aussi, `ballot` ; `adoptions.params` (JSON) garde de plus, pour l'acte, la
|
|
|
|
|
part simulée des votes d'entrée (`simulated`), la version déplacée (`displaced`), le cran
|
|
|
|
|
d'avant (`from_inertia`), et après l'acte `reexamined`.
|
|
|
|
|
|
|
|
|
|
Aucune ligne existante n'est réécrite, et le premier démarrage n'écrit rien du tampon (la
|
|
|
|
|
chaîne fermée en production : aucun bulletin). La purge (0008) efface avec un bulletin lâché
|
|
|
|
|
les messages qui en parlent.
|
|
|
|
|
|
|
|
|
|
## Coût
|
|
|
|
|
|
|
|
|
|
- **Les totaux montrés sont provisoires** le temps de la preuve : une intention compte pour la
|
|
|
|
|
session qui l'a demandée. Si une même identité vote depuis deux sessions à la fois, ou si une
|
|
|
|
|
session signe avec un autre portefeuille que celui de son vote précédent, les totaux montrés
|
|
|
|
|
peuvent s'écarter d'une voix jusqu'à la preuve ; la réconciliation remet le compte juste. Une
|
|
|
|
|
personne qui ouvre Monnaie libre peut faire paraître une voix dans les totaux montrés pendant
|
|
|
|
|
24 heures sans payer (une par session et par version, 20 bulletins au plus) : elle ne compte
|
|
|
|
|
jamais pour l'adoption.
|
|
|
|
|
- L'intention compte dès que le bulletin est scellé, quelques secondes avant la signature ; si
|
|
|
|
|
rien ne part, la page la lâche aussitôt.
|
|
|
|
|
- Un « pour » payé juste avant une objection mais lu juste après (l'indexeur suit à quelques
|
|
|
|
|
secondes) est écarté : la personne revote à la levée.
|
|
|
|
|
- **La simulation rend possible deux voix pour une personne** (une session par invitation, une
|
|
|
|
|
session Ğ1), ce que la chaîne empêche : accepté tant qu'elle dure, et effacé par l'acte
|
|
|
|
|
« supprime ». Chaque geste simulé laisse un bulletin en base jusqu'à l'acte.
|
|
|
|
|
- **Un texte entré par des votes simulés se lit en vigueur sur le site sans être publié** ; le
|
|
|
|
|
dépôt des textes garde la dernière publication jusqu'aux votes prouvés. L'acte peut faire
|
|
|
|
|
retourner parmi les propositions un texte que des personnes ont lu en vigueur.
|
|
|
|
|
- Un message sur un bulletin lâché disparaît avec lui à la purge (une semaine).
|