use std::collections::BTreeMap;
use super::{ContextDeltaError, ContextDeltaItemSnapshot, ContextDeltaItems, diff_item_fields};
#[path = "context_delta_apply.rs"]
mod apply;
#[path = "context_delta_wire.rs"]
mod wire;
type ItemIndex<'a> = BTreeMap<&'a str, (usize, &'a ContextDeltaItemSnapshot)>;
fn index_items<'a>(
items: &'a [ContextDeltaItemSnapshot],
side: &str,
) -> Result<ItemIndex<'a>, ContextDeltaError> {
let mut index = BTreeMap::new();
for (position, item) in items.iter().enumerate() {
let problem = if item.id.trim().is_empty() {
Some("blank item identity")
} else if index.insert(item.id.as_str(), (position, item)).is_some() {
Some("duplicate item identity")
} else {
None
};
if let Some(problem) = problem {
return Err(ContextDeltaError {
message: format!(
"{side} context snapshot has a {problem} at position {position}; emit the full pack instead"
),
});
}
}
Ok(index)
}
pub(super) fn diff_items(
prior_items: &[ContextDeltaItemSnapshot],
new_items: &[ContextDeltaItemSnapshot],
) -> Result<ContextDeltaItems, ContextDeltaError> {
let prior_by_id = index_items(prior_items, "prior")?;
let new_by_id = index_items(new_items, "new")?;
let mut retained_len = 0;
let mut last_prior_position = None;
for new_item in new_items {
let Some(&(position, prior_item)) = prior_by_id.get(new_item.id.as_str()) else {
break;
};
if last_prior_position.is_some_and(|last| position <= last)
|| prior_item
.fields
.keys()
.any(|field| !new_item.fields.contains_key(field))
{
break;
}
last_prior_position = Some(position);
retained_len += 1;
}
let added = new_items[retained_len..].to_vec();
let mut removed = Vec::new();
for id in prior_by_id.keys() {
if new_by_id
.get(*id)
.is_none_or(|(position, _)| *position >= retained_len)
{
removed.push((*id).to_owned());
}
}
let mut modified = new_items[..retained_len]
.iter()
.filter_map(|item| {
prior_by_id
.get(item.id.as_str())
.and_then(|(_, prior)| diff_item_fields(prior, item))
})
.collect::<Vec<_>>();
modified.sort_by(|left, right| left.id.cmp(&right.id));
Ok(ContextDeltaItems {
added,
removed,
modified,
})
}
#[cfg(test)]
mod tests {
use super::super::{
ContextDeltaFieldChange, ContextDeltaOptions, ContextDeltaPackSnapshot,
compute_context_delta,
};
use super::*;
use serde_json::json;
fn item(id: &str) -> ContextDeltaItemSnapshot {
ContextDeltaItemSnapshot::new(id)
.with_field("content", json!(format!("content for {id}")))
.with_field("provenance", json!({"source": id, "confidence": 0.8}))
}
fn items(ids: &[&str]) -> Vec<ContextDeltaItemSnapshot> {
ids.iter().map(|id| item(id)).collect()
}
fn apply_documented(
prior: &[ContextDeltaItemSnapshot],
delta: &ContextDeltaItems,
) -> Vec<ContextDeltaItemSnapshot> {
let mut result: Vec<_> = prior
.iter()
.filter(|item| !delta.removed.contains(&item.id))
.cloned()
.collect();
for change in &delta.modified {
let item = result
.iter_mut()
.find(|item| item.id == change.id)
.expect("modified item must survive removals");
for (field, change) in &change.field_changes {
let value = match change {
ContextDeltaFieldChange::Pair([_, new]) => new.clone(),
ContextDeltaFieldChange::Redacted(redaction) => redaction.new_value.clone(),
};
item.fields.insert(field.clone(), value);
}
}
for item in &delta.added {
assert!(result.iter().all(|existing| existing.id != item.id));
result.push(item.clone());
}
result
}
fn assert_roundtrip(
prior: &[ContextDeltaItemSnapshot],
new: &[ContextDeltaItemSnapshot],
) -> ContextDeltaItems {
let delta = diff_items(prior, new).expect("valid snapshots");
assert_eq!(apply_documented(prior, &delta), new);
assert_eq!(
delta
.apply_to_items(prior)
.expect("validated client application"),
new
);
delta
}
#[test]
fn additions_follow_the_new_pack_not_lexical_id_order() {
let new = items(&["ev_z", "mem_b", "rule_a"]);
let delta = assert_roundtrip(&[], &new);
assert_eq!(delta.added, new);
assert!(delta.removed.is_empty());
}
#[test]
fn a_prepended_item_repositions_the_existing_suffix() {
let prior = items(&["mem_a", "mem_b"]);
let new = items(&["ev_z", "mem_a", "mem_b"]);
let delta = assert_roundtrip(&prior, &new);
assert_eq!(delta.added, new);
assert_eq!(delta.removed, ["mem_a", "mem_b"]);
assert!(delta.modified.is_empty());
}
#[test]
fn rotation_retains_the_maximal_prefix_and_moves_only_the_tail() {
let prior = items(&["mem_a", "mem_b", "mem_c"]);
let new = items(&["mem_b", "mem_c", "mem_a"]);
let delta = assert_roundtrip(&prior, &new);
assert_eq!(delta.removed, ["mem_a"]);
assert_eq!(delta.added, items(&["mem_a"]));
assert!(delta.modified.is_empty());
}
#[test]
fn a_middle_insertion_preserves_the_unchanged_prefix() {
let prior = items(&["mem_a", "mem_b", "mem_c"]);
let new = items(&["mem_a", "ev_z", "mem_b", "mem_c"]);
let delta = assert_roundtrip(&prior, &new);
assert_eq!(delta.removed, ["mem_b", "mem_c"]);
assert_eq!(delta.added, new[1..]);
}
#[test]
fn deletion_only_does_not_resend_surviving_items() {
let prior = items(&["mem_a", "ev_z", "rule_b", "mem_c"]);
let new = items(&["mem_a", "mem_c"]);
let delta = assert_roundtrip(&prior, &new);
assert_eq!(delta.removed, ["ev_z", "rule_b"]);
assert!(delta.added.is_empty());
assert!(delta.modified.is_empty());
}
#[test]
fn append_and_field_updates_keep_the_compact_path() {
let prior = items(&["mem_a", "rule_b"]);
let mut new = items(&["mem_a", "rule_b", "ev_z"]);
new[1].fields.insert("confidence".into(), json!(0.9));
let delta = assert_roundtrip(&prior, &new);
assert!(delta.removed.is_empty());
assert_eq!(delta.added, items(&["ev_z"]));
assert_eq!(delta.modified.len(), 1);
assert_eq!(delta.modified[0].id, "rule_b");
}
#[test]
fn field_deletion_replaces_the_item_without_reemitting_the_removed_value() {
let mut prior = items(&["mem_a", "rule_b", "ev_z"]);
prior[1]
.fields
.insert("oldPolicy".into(), json!("private-obsolete-value"));
let new = items(&["mem_a", "rule_b", "ev_z"]);
let delta = assert_roundtrip(&prior, &new);
assert_eq!(delta.removed, ["ev_z", "rule_b"]);
assert_eq!(delta.added, new[1..]);
let wire = serde_json::to_string(&delta).expect("serialize item diff");
assert!(!wire.contains("private-obsolete-value"));
assert!(!wire.contains("oldPolicy"));
}
#[test]
fn absent_and_explicit_null_fields_roundtrip_in_both_directions() {
let absent = items(&["mem_a"]);
let mut present = absent.clone();
present[0].fields.insert("optional".into(), json!(null));
let insert = assert_roundtrip(&absent, &present);
assert!(insert.added.is_empty());
assert_eq!(insert.modified.len(), 1);
let remove = assert_roundtrip(&present, &absent);
assert_eq!(remove.removed, ["mem_a"]);
assert_eq!(remove.added, absent);
assert!(remove.modified.is_empty());
}
#[test]
fn duplicate_ids_fail_before_a_map_can_discard_evidence() {
for different_body in [false, true] {
let mut duplicate = item("private-duplicate-id");
if different_body {
duplicate
.fields
.insert("content".into(), json!("private-other-body"));
}
let invalid = [item("private-duplicate-id"), duplicate];
for (prior, new) in [(&invalid[..], &[][..]), (&[][..], &invalid[..])] {
let error = diff_items(prior, new).expect_err("ambiguous identities");
let text = error.to_string();
assert!(text.contains("duplicate item identity"));
assert!(!text.contains("private-duplicate-id"));
assert!(!text.contains("private-other-body"));
}
}
}
#[test]
fn blank_ids_fail_on_either_snapshot() {
for id in ["", " ", "\t\n"] {
let invalid = [item(id)];
assert!(diff_items(&invalid, &[]).is_err());
assert!(diff_items(&[], &invalid).is_err());
}
}
#[test]
fn public_kernel_preserves_order_fields_hashes_and_input_immutability() {
let prior = ContextDeltaPackSnapshot::new(
"blake3:prior",
10,
1_000_000,
100,
items(&["mem_a", "ev_z"]),
);
let mut new = ContextDeltaPackSnapshot::new(
"blake3:new",
11,
1_000_000,
90,
items(&["ev_z", "mem_a"]),
);
new.items[0].fields.remove("provenance");
let prior_copy = prior.clone();
let new_copy = new.clone();
let envelope = compute_context_delta(&prior, &new, ContextDeltaOptions::new(None))
.expect("public delta computation");
assert!(envelope.emits_delta());
assert_eq!(
apply_documented(&prior.items, &envelope.data.items),
new.items
);
assert_eq!(envelope.data.prior_pack_hash, prior.pack_hash);
assert_eq!(envelope.data.new_pack_hash, new.pack_hash);
assert_eq!(envelope.data.token_savings.net_pack_tokens, 90);
assert_eq!(prior, prior_copy);
assert_eq!(new, new_copy);
assert!(
!envelope
.data
.server_decision
.computed_from_server_verified_pack_record
);
}
fn ordered_subsets(
prefix: Vec<&'static str>,
remaining: &[&'static str],
out: &mut Vec<Vec<&'static str>>,
) {
out.push(prefix.clone());
for (index, id) in remaining.iter().enumerate() {
let mut next = prefix.clone();
next.push(*id);
let rest: Vec<_> = remaining
.iter()
.enumerate()
.filter(|(other, _)| *other != index)
.map(|(_, id)| *id)
.collect();
ordered_subsets(next, &rest, out);
}
}
#[test]
fn all_ordered_subset_transitions_reconstruct_exactly_with_and_without_field_changes() {
let mut sequences = Vec::new();
ordered_subsets(
Vec::new(),
&["mem_a", "ev_z", "rule_b", "mem_c"],
&mut sequences,
);
assert_eq!(sequences.len(), 65);
let mut transitions = 0;
for prior_ids in &sequences {
for new_ids in &sequences {
let prior = items(prior_ids);
let new = items(new_ids);
assert_roundtrip(&prior, &new);
let mut changed = new.clone();
for (index, item) in changed.iter_mut().enumerate() {
item.fields.insert("rank".into(), json!(index + 1));
if index % 2 == 0 {
item.fields.remove("provenance");
} else {
item.fields.insert("optional".into(), json!(null));
}
}
assert_roundtrip(&prior, &changed);
transitions += 2;
}
}
assert_eq!(transitions, 8_450);
}
}