From 20fb808e7bcfcc8f8c4e51122d4733a3ed5fd741 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 31 Oct 2026 09:05:26 +0100 Subject: [PATCH] tempo: an explicit null vote withdraws the standing one; the Pulse says so in its own field and the reply may omit the vote key Co-Authored-By: Claude Sonnet 5.5 --- crates/eureka-pipeline/src/lib.rs | 25 ++++++++---- crates/huginn-mind/src/admission.rs | 2 +- crates/huginn-mind/src/query.rs | 4 +- crates/huginn-mind/src/rows.rs | 4 +- crates/mind-body/src/heartbeat.rs | 7 ++-- crates/mind-body/src/heartbeat/tests.rs | 27 +++++++++++++ crates/mind-body/src/persona.rs | 21 ++++++---- crates/mind-body/src/persona/tests.rs | 51 +++++++++++++++++-------- crates/mind-body/src/tempo.rs | 5 ++- 9 files changed, 108 insertions(+), 38 deletions(-) diff --git a/crates/eureka-pipeline/src/lib.rs b/crates/eureka-pipeline/src/lib.rs index a071572..11f4f65 100644 --- a/crates/eureka-pipeline/src/lib.rs +++ b/crates/eureka-pipeline/src/lib.rs @@ -96,6 +96,12 @@ impl Absent for Option { } } +impl Absent for bool { + fn absent(&self) -> bool { + !*self + } +} + impl Absent for Vec { fn absent(&self) -> bool { self.is_empty() @@ -591,7 +597,10 @@ fn pulse_is_well_formed(pulse: &PipelinePulse, at: &str) -> Result<(), PipelineR { return Err(format_error(&format!("{at}.urgency_permille"), &urgency.to_string())); } - if pulse.tempo_vote_beat_s.is_none() && pulse.urgency_permille.is_none() { + if pulse.tempo_vote_withdrawn && pulse.tempo_vote_beat_s.is_some() { + return Err(format_error(&format!("{at}.tempo_vote_withdrawn"), "a pulse that votes a beat and withdraws the vote")); + } + if pulse.tempo_vote_beat_s.is_none() && !pulse.tempo_vote_withdrawn && pulse.urgency_permille.is_none() { return Err(format_error(&format!("{at}.tempo_vote_beat_s"), "a pulse that votes and declares nothing")); } Ok(()) @@ -736,12 +745,14 @@ value_types! { budget_usd: Short, claims: Vec[64], campaigns: Vec[8], } => run_is_well_formed /// One Mind's heartbeat vote, in her own mind: the beat she wants (the swarm's - /// tempo is the lower median of every Mind's newest vote) and the urgency she - /// declares for herself until her next Persona turn. Either may be absent, not - /// both. Instance-rooted: the key is `:pulse: