ive harnessed the harness
Something went wrong. Try again.
9.8 kB · 285 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286//! Content identity and deployment seed three-way reconciliation (P13, CONTRACTS.md Section 10).//!//! Enforces://! - Deployment seed does not overwrite live learning on restart or upgrade.//! - Selected vs applied revisions remain distinct.//! - Three-way reconciliation (base_seed, live_selection, new_seed) reports conflicts//! without overwriting either side.//! - Broken candidate start preserves the last good worker/revision.
use serde::{Deserialize, Serialize};
/// Deployment seed specifying a baseline configuration or revision.#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]pub struct DeploymentSeed { pub seed_id: String, pub revision: String, pub source: String, pub created_at_ms: i64,}
impl DeploymentSeed { /// A seed stamped with the crate's own clock. /// /// `created_at_ms` is derived, not carried, which is fine for a live seed but /// useless to a gate that has to reason about a specific deployment: use [`Self::at`] /// when the timestamp is part of what is being asserted. #[must_use] pub fn new( seed_id: impl Into<String>, revision: impl Into<String>, source: impl Into<String>, ) -> Self { Self::at( seed_id, revision, source, crate::store::now_ms().unwrap_or_default(), ) }
/// A seed with an explicit timestamp. Reconciliation is about revisions, so a /// caller that asserts on an ordering or a replay supplies the instant rather /// than reading the wall clock. #[must_use] pub fn at( seed_id: impl Into<String>, revision: impl Into<String>, source: impl Into<String>, created_at_ms: i64, ) -> Self { Self { seed_id: seed_id.into(), revision: revision.into(), source: source.into(), created_at_ms, } }}
/// Explicit conflict during three-way seed reconciliation.#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]pub struct ReconciliationConflict { pub base_seed: String, pub live_selection: String, pub new_seed: String, pub message: String,}
/// Outcome of three-way reconciliation.#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]#[serde(tag = "action", rename_all = "snake_case")]pub enum ReconciliationOutcome { /// Same seed: live selection is preserved untouched. Preserved { effective_revision: String }, /// Clean fast-forward: live state had not diverged from base seed. FastForward { effective_revision: String }, /// Both sides converged to the same revision. Converged { effective_revision: String }, /// Explicit conflict: neither side is overwritten. Conflict { effective_revision: String, conflict: ReconciliationConflict, },}
impl ReconciliationOutcome { #[must_use] pub fn effective_revision(&self) -> &str { match self { Self::Preserved { effective_revision } | Self::FastForward { effective_revision } | Self::Converged { effective_revision } | Self::Conflict { effective_revision, .. } => effective_revision.as_str(), } }
#[must_use] pub fn is_conflict(&self) -> bool { matches!(self, Self::Conflict { .. }) }}
/// Reconciles an explicit deployment seed change against live selection using 3-way reconciliation.#[must_use]pub fn reconcile_seed( base_seed: &str, live_selection: &str, new_seed: &str,) -> ReconciliationOutcome { if new_seed == base_seed { return ReconciliationOutcome::Preserved { effective_revision: live_selection.to_string(), }; } if live_selection == base_seed { return ReconciliationOutcome::FastForward { effective_revision: new_seed.to_string(), }; } if new_seed == live_selection { return ReconciliationOutcome::Converged { effective_revision: live_selection.to_string(), }; } let conflict = ReconciliationConflict { base_seed: base_seed.to_string(), live_selection: live_selection.to_string(), new_seed: new_seed.to_string(), message: format!( "three-way reconciliation conflict: base seed '{base_seed}', live selection '{live_selection}', new seed '{new_seed}'" ), }; ReconciliationOutcome::Conflict { effective_revision: live_selection.to_string(), conflict, }}
/// Explicit tracking of selected vs actually applied revisions.#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]pub struct RevisionStatus { pub selected_revision: String, pub applied_revision: String, pub last_good_revision: String,}
impl RevisionStatus { #[must_use] pub fn new(initial: impl Into<String>) -> Self { let rev = initial.into(); Self { selected_revision: rev.clone(), applied_revision: rev.clone(), last_good_revision: rev, } }
/// Update the selected revision (desired state). pub fn select(&mut self, new_revision: impl Into<String>) { self.selected_revision = new_revision.into(); }
/// Commit successful application of the candidate. pub fn apply_success(&mut self, applied: impl Into<String>) { let rev = applied.into(); self.applied_revision = rev.clone(); self.last_good_revision = rev; }
/// Handle failed candidate start (negative/control): preserves last good revision! pub fn apply_failure(&mut self, _failed_revision: &str) { // Does NOT update applied_revision to the broken candidate! // Preserves the last good worker/revision. self.applied_revision = self.last_good_revision.clone(); }
#[must_use] pub fn is_in_sync(&self) -> bool { self.selected_revision == self.applied_revision }}
#[cfg(test)]mod tests { use super::*;
#[test] fn test_same_seed_preserves_live_state() { let base_seed = "rev-seed-v1"; let live_selection = "rev-live-learning-v2"; let outcome = reconcile_seed(base_seed, live_selection, base_seed); assert_eq!( outcome, ReconciliationOutcome::Preserved { effective_revision: live_selection.to_string(), } ); assert_eq!(outcome.effective_revision(), live_selection); assert!(!outcome.is_conflict()); }
#[test] fn test_fast_forward_when_no_live_learning() { let base_seed = "rev-seed-v1"; let live_selection = "rev-seed-v1"; // no divergence let new_seed = "rev-seed-v2"; let outcome = reconcile_seed(base_seed, live_selection, new_seed); assert_eq!( outcome, ReconciliationOutcome::FastForward { effective_revision: new_seed.to_string(), } ); assert_eq!(outcome.effective_revision(), new_seed); assert!(!outcome.is_conflict()); }
#[test] fn test_conflicting_seed_reports_conflict_without_overwriting() { let base_seed = "rev-seed-v1"; let live_selection = "rev-live-learning-v2"; let new_seed = "rev-seed-v3"; let outcome = reconcile_seed(base_seed, live_selection, new_seed); assert!(outcome.is_conflict()); assert_eq!(outcome.effective_revision(), live_selection); // Live selection preserved! if let ReconciliationOutcome::Conflict { effective_revision, conflict, } = outcome { assert_eq!(effective_revision, live_selection); assert_eq!(conflict.base_seed, base_seed); assert_eq!(conflict.live_selection, live_selection); assert_eq!(conflict.new_seed, new_seed); assert!(conflict .message .contains("three-way reconciliation conflict")); } else { panic!("expected conflict"); } }
#[test] fn test_converged_seed_and_live_selection() { let base_seed = "rev-seed-v1"; let live_selection = "rev-seed-v2"; let new_seed = "rev-seed-v2"; let outcome = reconcile_seed(base_seed, live_selection, new_seed); assert_eq!( outcome, ReconciliationOutcome::Converged { effective_revision: live_selection.to_string(), } ); }
#[test] fn test_selected_and_applied_revisions_remain_distinct_and_broken_start_preserves_last_good() { let mut status = RevisionStatus::new("rev-initial"); assert!(status.is_in_sync()); assert_eq!(status.selected_revision, "rev-initial"); assert_eq!(status.applied_revision, "rev-initial");
// Adopt a new revision: selected changes immediately, applied remains initial status.select("rev-adopted"); assert!(!status.is_in_sync()); assert_eq!(status.selected_revision, "rev-adopted"); assert_eq!(status.applied_revision, "rev-initial");
// Negative/control: Broken candidate start fails status.apply_failure("rev-adopted-broken"); assert!(!status.is_in_sync()); assert_eq!(status.selected_revision, "rev-adopted"); assert_eq!(status.applied_revision, "rev-initial"); // preserved last good!
// Successful candidate application status.apply_success("rev-adopted"); assert!(status.is_in_sync()); assert_eq!(status.selected_revision, "rev-adopted"); assert_eq!(status.applied_revision, "rev-adopted"); assert_eq!(status.last_good_revision, "rev-adopted"); }}