forked from yvv/decision
Lot S, phase 2 : le quorum dans le serveur (ADR 0013), vu rouge puis vert
- migration 0011 (additive ; 0010 reste au lot V) : documents.quorums (par cran,
NULL = vote_floor à chaque cran, le C d'avant), units.quorum (NULL = celui du
cran), journaux quorum_changes et quorum_amendments ;
- quorum effectif = units.quorum ?? quorums[cran] ; les propositions d'inertie et
les modules en héritent ; p0 = 2 × vote_floor / W, jamais le quorum d'un article ;
- route PUT /units/{id}/quorum { "quorum": n | null } : trustin_access::sets_quorum
(l'auteur de la première version, un administrateur pour le seed), refusée dès
qu'un vote existe (quorum::voted_units : vote du serveur, bulletin de la chaîne
en attente ou confirmé, adoption) ; bornes 2..W, 1..W dans le groupe d'essai ;
- amendement gardé 2026-11-03-quorums-par-cran : écrit les quorums du seed là où
il n'y en a pas, une fois par document, consigné, file de publication là où il
publie ;
- JSON : evaluation.quorum / yesNeeded / noNeeded ; l'unité : quorum,
quorumDefault, canSetQuorum ; doc.quorums ; levels[].quorum ; document.json
publié : quorums ; adoptions.params : quorum (plus de constant) ;
- seed : quorums 12 · 12 · 50 · 50 · 50 (Monnaie libre), 1 partout (essai).
- tests : tests/quorum.rs (5) et les règles pures ; les scénarios du groupe
d'essai qui demandaient deux voix posent le quorum 2 de l'article, comme son
auteur le ferait ; les chiffres de Monnaie libre suivent (9 au cran 2, 39 au
cran 3, 43 au cran 4).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,10 @@
|
||||
//! administrator; it is reiterated by the person who raised it only —
|
||||
//! `lifts_objection` / `reiterates_objection`;
|
||||
//! - each person reads their own messages only — `reads_message`
|
||||
//! (2026-10-31, lot F, ADR 0010).
|
||||
//! (2026-10-31, lot F, ADR 0010);
|
||||
//! - the quorum of an article is set by the author of its first version — for a
|
||||
//! text of the seed, by an administrator — and only before the first vote —
|
||||
//! `sets_quorum` (2026-11-03, ADR 0013).
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -184,6 +187,24 @@ pub fn reads_message(identity: &Identity, recipient: &str) -> bool {
|
||||
identity.id == recipient
|
||||
}
|
||||
|
||||
/// Who sets the quorum of an article (Yvv, 2026-11-02: « L'auteur, avant le 1er vote »,
|
||||
/// ADR 0013): the author of its first version; for an article without author (a text of
|
||||
/// the seed), an administrator of trustinWallet — never one for an author. Once a vote
|
||||
/// exists on the article (`voted`, the server's `quorum::a_vote_exists`), nobody: the
|
||||
/// votes cast were judged on it.
|
||||
pub fn sets_quorum(identity: &Identity, first_author: Option<&str>, voted: bool) -> bool {
|
||||
if voted {
|
||||
return false;
|
||||
}
|
||||
match first_author {
|
||||
Some(author) => identity.id == author,
|
||||
None => identity.admin,
|
||||
}
|
||||
}
|
||||
|
||||
/// The test group: hidden, the quorum 1 at every level — the one place one voice passes.
|
||||
pub const TEST_GROUP: &str = "essai";
|
||||
|
||||
/// The groups a fresh deployment starts with. Ordered as displayed.
|
||||
pub fn default_groups() -> Vec<Group> {
|
||||
let g = |slug: &str, name: &str, parent: Option<&str>, policy, hidden| Group {
|
||||
@@ -204,7 +225,7 @@ pub fn default_groups() -> Vec<Group> {
|
||||
GroupPolicy::Managed,
|
||||
false,
|
||||
),
|
||||
g("essai", "Groupe d'essai", None, GroupPolicy::Managed, true),
|
||||
g(TEST_GROUP, "Groupe d'essai", None, GroupPolicy::Managed, true),
|
||||
]
|
||||
}
|
||||
|
||||
@@ -308,6 +329,20 @@ mod tests {
|
||||
assert!(!lifts_objection(&other, "luna") && !reiterates_objection(&other, "luna"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_author_sets_the_quorum_before_the_first_vote_an_administrator_for_the_seed() {
|
||||
let chiara = Identity { id: "chiara".into(), ..who(IdentityKind::Invite, false, false) };
|
||||
let admin = Identity { id: "yvv".into(), ..who(IdentityKind::G1, true, true) };
|
||||
let other = Identity { id: "luna".into(), ..who(IdentityKind::G1, true, false) };
|
||||
assert!(sets_quorum(&chiara, Some("chiara"), false));
|
||||
assert!(!sets_quorum(&chiara, Some("chiara"), true), "after the first vote, nobody");
|
||||
assert!(!sets_quorum(&other, Some("chiara"), false));
|
||||
assert!(!sets_quorum(&admin, Some("chiara"), false), "an administrator never sets it for an author");
|
||||
assert!(sets_quorum(&admin, None, false), "a text of the seed: an administrator");
|
||||
assert!(!sets_quorum(&admin, None, true));
|
||||
assert!(!sets_quorum(&other, None, false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_message_is_read_by_its_recipient_only() {
|
||||
let admin = Identity { id: "yvv".into(), ..who(IdentityKind::G1, true, true) };
|
||||
|
||||
Reference in New Issue
Block a user