Lot V : « Pas avant des votes prouvés » (Yvv, 03/11) — un texte entré par des votes simulés est en vigueur sur le site, la publication attend que tout ce qui est en vigueur tienne sur les seuls votes prouvés (publish::waits_for_proven_votes, simulation::unproven), LD_PUBLISH_SIMULATED=1 publie quand même ; publication.waitsFor ; vu rouge puis vert

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-11-03 05:42:13 +01:00
co-authored by Claude Opus 5.5
parent 0cde5bdbd0
commit 9b7345346e
7 changed files with 258 additions and 25 deletions
+70 -7
View File
@@ -83,18 +83,29 @@ pub async fn pass(state: &AppState) -> anyhow::Result<()> {
let rows: Vec<&Due> = due.iter().filter(|d| d.doc_id == doc_id).collect();
let last = rows.iter().map(|d| d.id).max().unwrap_or(0);
let attempts = rows.iter().map(|d| d.attempts).max().unwrap_or(0);
if waits_for_a_text(state, &doc_id).await? {
if let Some(wait) = waits(state, &doc_id).await? {
// Not a failure: no attempt counted, no error. The queue stays; the first
// text that enters queues a row due now, and everything goes out with it.
// text that enters queues a row due now, and everything goes out with it —
// or, waiting for proven votes, it is asked again a while later.
let again = match wait {
Wait::Text => MAX_BACKOFF_SECS,
Wait::Proven => PROVEN_AGAIN_SECS,
};
sqlx::query("UPDATE outbox SET next_at = ? WHERE doc_id = ? AND done_at IS NULL")
.bind(util::in_seconds(MAX_BACKOFF_SECS))
.bind(util::in_seconds(again))
.bind(&doc_id)
.execute(&state.db)
.await?;
tracing::info!(
doc = %doc_id, waiting = rows.len(),
"publication suspended: no article in force, the queue waits for the first text (LD_PUBLISH_WITHOUT_ARTICLES=1 publishes anyway)"
);
match wait {
Wait::Text => tracing::info!(
doc = %doc_id, waiting = rows.len(),
"publication suspended: no article in force, the queue waits for the first text (LD_PUBLISH_WITHOUT_ARTICLES=1 publishes anyway)"
),
Wait::Proven => tracing::info!(
doc = %doc_id, waiting = rows.len(),
"publication suspended: something in effect entered by simulated votes does not hold on the proven votes (LD_PUBLISH_SIMULATED=1 publishes anyway)"
),
}
continue;
}
match publish_doc(state, &doc_id).await {
@@ -123,6 +134,58 @@ pub async fn pass(state: &AppState) -> anyhow::Result<()> {
Ok(())
}
/// Why a document waits before it is published.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Wait {
/// None of its articles is in force (ADR 0011).
Text,
/// Something in effect entered by simulated votes and does not hold on the
/// proven votes alone (ADR 0012).
Proven,
}
impl Wait {
/// As the API says it (`publication.waitsFor`).
pub fn as_str(self) -> &'static str {
match self {
Self::Text => "text",
Self::Proven => "proven",
}
}
}
/// Waiting for proven votes, the queue is asked again this often: a proven vote that
/// carries an entry already in force queues nothing by itself.
const PROVEN_AGAIN_SECS: i64 = 600;
/// Does the document wait before it is published, and for what: a first text
/// (`waits_for_a_text`), then proven votes (`waits_for_proven_votes`).
pub async fn waits(state: &AppState, doc_id: &str) -> anyhow::Result<Option<Wait>> {
if waits_for_a_text(state, doc_id).await? {
return Ok(Some(Wait::Text));
}
if waits_for_proven_votes(state, doc_id).await? {
return Ok(Some(Wait::Proven));
}
Ok(None)
}
/// Does the document wait for proven votes before it is published? Yvv, 2026-11-03:
/// « Pas avant des votes prouvés ». A text — an order, a title, a level of inertia, a
/// removal — that entered by simulated votes is in force on the site, and sends
/// nothing to the repository of the texts while anything in effect that entered by
/// simulated votes does not hold on the proven votes alone, by the re-examination of
/// the act « supprime » (`simulation::unproven`, told only). The chain being closed,
/// nothing goes out of Monnaie libre before it opens. The one place of this rule;
/// `LD_PUBLISH_SIMULATED=1` publishes anyway (`Publish::simulated`).
pub async fn waits_for_proven_votes(state: &AppState, doc_id: &str) -> anyhow::Result<bool> {
if state.cfg.publish.as_ref().is_none_or(|p| p.simulated) {
return Ok(false);
}
let unproven = crate::simulation::unproven(state, doc_id).await.map_err(|e| anyhow::anyhow!("{e}"))?;
Ok(!unproven.is_empty())
}
/// Does the document wait for a text before it is published? Yes when none of its
/// articles is in force — the seed proposals are not (ADR 0011) — unless the
/// publication is told to go out anyway (`LD_PUBLISH_WITHOUT_ARTICLES`). The default is