v3 : sur une version retenue, « contre » et retrait oui, « pour » non (moteur, serveur, chaîne)

Décision d'Yvv du 01/11/2026 : « Retirer ou "contre" ». Le moteur dit la règle
(objection::may_cast) ; le vote direct, le vote groupé et le bulletin de la chaîne
l'appliquent par DocState::admits : « pour » 409 objection, « contre » et retrait
acceptés, une ligne refusée refuse tout le vote. Un bulletin scellé avant
l'objection compte tel qu'il est signé ; une version retenue n'entre jamais.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-11-02 00:03:45 +01:00
co-authored by Claude Opus 5.5
parent 61547c11c0
commit 238c7db93f
8 changed files with 222 additions and 19 deletions
+8 -7
View File
@@ -648,19 +648,20 @@ pub async fn vote(
if !matches!(p.status.as_str(), "open" | "in_force") {
return Err(AppError::Conflict("not_votable"));
}
// Held back by an objection: its votes stay, no new one (ADR 0010).
if !st.votable(&p) {
return Err(AppError::Conflict("objection"));
}
if !docs::may_vote(&state, &user, &st.group).await? {
return Err(AppError::Conflict("members_only"));
}
let choice = match b.choice.as_deref() {
Some("for") => Choice::For,
Some("against") => Choice::Against,
None => Choice::None,
Some(_) => return Err(AppError::Invalid("choice")),
};
// Held back by an objection: its votes stay; against or a withdrawal, never a
// new *for* (ADR 0010, Yvv 2026-11-01: « Retirer ou "contre" »).
if !st.admits(&p, choice) {
return Err(AppError::Conflict("objection"));
}
if !docs::may_vote(&state, &user, &st.group).await? {
return Err(AppError::Conflict("members_only"));
}
let cast = ballots::Cast { proposal_id: p.id.clone(), revision: p.revision, choice };
ballots::write_server_votes(&state, &user.id, &[cast]).await?;
let entered = adoption::run(&state, &st.doc.id).await?;