v3 : serveur des objections, des coquilles validées et de la messagerie (lot F)

Objections « redit » / « contredit » à la place des signalements : la
dernière arrivée des deux versions n'est plus votable (repli, panier,
chaîne : un vote déjà scellé est gardé mais ne la fait pas entrer) ; un
texte en vigueur ne l'est jamais. L'auteur répond en modifiant l'article
visé (proposition liée) ou en reformulant ; la personne qui objecte lève
ou réitère depuis ses messages ; sans réponse sous 3 jours, l'objection
est levée. Un administrateur peut lever.

Coquilles : la sienne passe tout de suite ; celle d'un autre attend
l'auteur (valider / refuser), passe seule au bout de 3 jours, et l'auteur
garde la main tant que le texte n'est pas en vigueur. En vigueur, seule
une coquille validée corrige (auteur de la version, administrateur pour
un texte semé). Deux coquilles en attente : la seconde se mesure au texte
voté ; à l'application, celle qui ne colle plus est périmée.

Messagerie dans l'application (boîte, non-lus, gestes, échéance), chaque
personne ne lit que les siens. Échéances par le balayage existant, avec
rattrapage au démarrage et horloge injectable (route de dev). Migration
0008 additive : les signalements deviennent des « contredit », sans perte.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-31 02:45:48 +01:00
co-authored by Claude Opus 5.5
parent 70dbd712c8
commit 3f1f968f16
17 changed files with 2754 additions and 238 deletions
+6 -1
View File
@@ -203,7 +203,8 @@ fn kept_params(st: &DocState, unit: &docs::UnitRow, p: &docs::ProposalRow) -> se
/// The electorate changes (the web of trust grows or shrinks): every few
/// minutes, every document is passed through the engine again — after the
/// usual participation of each group is measured again, once a day.
/// usual participation of each group is measured again, once a day, and after
/// the deadlines of three days are passed (`deadlines`).
pub async fn sweeper(state: AppState) {
let mut tick = tokio::time::interval(Duration::from_secs(300));
loop {
@@ -211,6 +212,10 @@ pub async fn sweeper(state: AppState) {
if let Err(e) = crate::participation::refresh(&state, false).await {
tracing::warn!(error = %e, "usual participation");
}
// Objections lifted and typo fixes applied at their deadline (three days).
if let Err(e) = crate::deadlines::sweep(&state).await {
tracing::warn!(error = %e, "deadlines");
}
let ids: Vec<String> = match sqlx::query_scalar("SELECT id FROM documents").fetch_all(&state.db).await {
Ok(ids) => ids,
Err(e) => {