v3 : relecture de cd7e683 — coquilles, objections et retraits
ci/woodpecker/push/app Pipeline was successful
ci/woodpecker/push/maquette Pipeline was successful

- Le revirement de l'auteur sur une coquille (reprendre une correction appliquée,
  valider une correction refusée) ne s'écrit que sur une version encore ouverte :
  Settle porte while_open ; et la garde de statut de write_text marche enfin
  (?9 restait NULL, le statut est le 8e paramètre).
- 0008 lève les signalements debout dont un côté est déjà retiré.
- Une seule objection debout par personne et par paire (index unique partiel) ;
  envoyée plusieurs fois à la fois, les autres reçoivent objection_exists.
- Un retrait relance l'adoption : la version que l'objection retenait entre aussitôt.

Tests vus rouges d'abord. Boucle : 230 tests Rust, svelte-check 0, 55 vitest,
e2e 35 / 35.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-10-31 10:00:29 +01:00
co-authored by Claude Opus 5.5
parent 033b331228
commit 4d4adcc281
8 changed files with 220 additions and 20 deletions
+18 -12
View File
@@ -88,6 +88,9 @@ pub enum TextChange {
pub struct Settle {
pub status: TypoStatus,
pub text: TextChange,
/// Holds only while the version is not in force: the author's change of mind,
/// decided on a version read open, must not land on one that entered meanwhile.
pub while_open: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -98,21 +101,24 @@ pub enum TypoRefusal {
/// An answer of the one who answers for the version. `in_force`: the version is in force now.
pub fn answer(status: TypoStatus, answer: Answer, in_force: bool) -> Result<Settle, TypoRefusal> {
let to = |status, text| Ok(Settle { status, text });
let to = |status, text, while_open| Ok(Settle { status, text, while_open });
match (status, answer) {
(TypoStatus::Pending, Answer::Validate) => to(TypoStatus::Applied, TextChange::Write),
(TypoStatus::Pending, Answer::Refuse) => to(TypoStatus::Refused, TextChange::Keep),
(TypoStatus::Pending, Answer::Validate) => to(TypoStatus::Applied, TextChange::Write, false),
(TypoStatus::Pending, Answer::Refuse) => to(TypoStatus::Refused, TextChange::Keep, false),
// The author keeps the hand while the version is not in force.
(TypoStatus::Applied, Answer::Refuse) if !in_force => to(TypoStatus::Refused, TextChange::Restore),
(TypoStatus::Refused, Answer::Validate) if !in_force => to(TypoStatus::Applied, TextChange::Write),
(TypoStatus::Applied, Answer::Refuse) if !in_force => to(TypoStatus::Refused, TextChange::Restore, true),
(TypoStatus::Refused, Answer::Validate) if !in_force => to(TypoStatus::Applied, TextChange::Write, true),
_ => Err(TypoRefusal::Settled),
}
}
/// The clock (sortable stamps): a pending fix applies by itself at its deadline.
pub fn tick(status: TypoStatus, due: &str, now: &str) -> Option<Settle> {
(status == TypoStatus::Pending && due <= now)
.then_some(Settle { status: TypoStatus::Applied, text: TextChange::Write })
(status == TypoStatus::Pending && due <= now).then_some(Settle {
status: TypoStatus::Applied,
text: TextChange::Write,
while_open: false,
})
}
/// A text as a fix sees it: the revision it belongs to, its title and body.
@@ -145,11 +151,11 @@ mod tests {
for in_force in [false, true] {
assert_eq!(
answer(TypoStatus::Pending, Answer::Validate, in_force),
Ok(Settle { status: TypoStatus::Applied, text: TextChange::Write })
Ok(Settle { status: TypoStatus::Applied, text: TextChange::Write, while_open: false })
);
assert_eq!(
answer(TypoStatus::Pending, Answer::Refuse, in_force),
Ok(Settle { status: TypoStatus::Refused, text: TextChange::Keep })
Ok(Settle { status: TypoStatus::Refused, text: TextChange::Keep, while_open: false })
);
}
}
@@ -159,11 +165,11 @@ mod tests {
// A proposal: an applied fix may be taken back, a refused one validated after all.
assert_eq!(
answer(TypoStatus::Applied, Answer::Refuse, false),
Ok(Settle { status: TypoStatus::Refused, text: TextChange::Restore })
Ok(Settle { status: TypoStatus::Refused, text: TextChange::Restore, while_open: true })
);
assert_eq!(
answer(TypoStatus::Refused, Answer::Validate, false),
Ok(Settle { status: TypoStatus::Applied, text: TextChange::Write })
Ok(Settle { status: TypoStatus::Applied, text: TextChange::Write, while_open: true })
);
// In force: an answer is final.
assert_eq!(answer(TypoStatus::Applied, Answer::Refuse, true), Err(TypoRefusal::Settled));
@@ -182,7 +188,7 @@ mod tests {
assert_eq!(tick(TypoStatus::Pending, due, "2026-11-03T08:59:59Z"), None);
assert_eq!(
tick(TypoStatus::Pending, due, due),
Some(Settle { status: TypoStatus::Applied, text: TextChange::Write })
Some(Settle { status: TypoStatus::Applied, text: TextChange::Write, while_open: false })
);
// A deadline passed while the server was stopped is taken at the next tick.
assert!(tick(TypoStatus::Pending, due, "2027-02-01T00:00:00Z").is_some());