Fusion de chantier/acte-certification (lots H et S) dans le lot V : le tampon et ses états, avec le quorum et les Propositions seed

- conflits résolus des deux côtés : kept_params garde le quorum (S) et la part simulée, la version déplacée, le cran d'avant (V) ; proposals.rs garde seedMinimal / seedProposal (S) et tallyPending / tallySimulated / myVoteState (V) ;
- simulation::reexamine juge sur le quorum gardé par l'adoption (params.quorum, à défaut celui de l'article) ;
- l'acte répété n'examine plus une adoption qui tient (params.reexamined) ;
- tests V repris sans texte en vigueur au départ (I1, E1 : la Proposition seed) et sur un quorum 2 (small_web) ; le test overview d'une personne invitée dans Monnaie libre suit la simulation (elle vote, chaîne fermée).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-11-03 05:32:51 +01:00
co-authored by Claude Opus 5.5
66 changed files with 5134 additions and 701 deletions
+17 -7
View File
@@ -322,7 +322,10 @@ pub struct DevChainBody {
/// `POST /api/dev/vote-chain` — tests only, with the dev login: closes (or opens
/// again) the chain of this server, to try the simulation (ADR 0012).
pub async fn dev_chain(State(state): State<AppState>, Json(b): Json<DevChainBody>) -> AppResult<Json<serde_json::Value>> {
pub async fn dev_chain(
State(state): State<AppState>,
Json(b): Json<DevChainBody>,
) -> AppResult<Json<serde_json::Value>> {
if !state.cfg.dev_login {
return Err(AppError::NotFound);
}
@@ -746,8 +749,11 @@ pub async fn check(state: &AppState, row: &BallotRow) -> AppResult<()> {
match verdict {
// The right to vote is checked now, at confirmation, by trustinWallet's rule.
Verdict::Paid { identity } if trustin_access::chain_vote_counts(identity.member) => {
let proof =
Proof { voter: format!("idty:{}", identity.index), position: Some(found.comment.position), found: Some(found) };
let proof = Proof {
voter: format!("idty:{}", identity.index),
position: Some(found.comment.position),
found: Some(found),
};
settle(state, row, proof).await.map(|_| ())
}
Verdict::Paid { .. } => refuse(state, row, found, "not_member").await,
@@ -921,9 +927,10 @@ pub async fn reconcile(conn: &mut SqliteConnection, voter: &str, proposal_id: &s
.await?;
let mut kept: Vec<(&Row, Proven)> = Vec::with_capacity(rows.len());
for r in &rows {
let position = r.block.zip(r.event_index).and_then(|(b, e)| {
Some(ChainPosition { block: u32::try_from(b).ok()?, event: u32::try_from(e).ok()? })
});
let position = r
.block
.zip(r.event_index)
.and_then(|(b, e)| Some(ChainPosition { block: u32::try_from(b).ok()?, event: u32::try_from(e).ok()? }));
let (Some(position), Some(choice)) = (position, Choice::parse(&r.choice)) else {
tracing::warn!(ballot = %r.ballot_id, "a proven line that does not read back");
continue;
@@ -1218,7 +1225,10 @@ pub async fn intents(state: &AppState, doc_id: &str) -> AppResult<Vec<Intent>> {
for r in rows {
let Some(choice) = Choice::parse(&r.choice) else { continue };
let key = (r.requested_by.clone(), r.proposal_id.clone());
latest.insert(key, Intent { requester: r.requested_by, proposal_id: r.proposal_id, revision: r.revision, choice });
latest.insert(
key,
Intent { requester: r.requested_by, proposal_id: r.proposal_id, revision: r.revision, choice },
);
}
Ok(latest.into_values().collect())
}