aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-08 15:02:24 +0000
committertv <tv@krebsco.de>2026-11-08 16:07:29 +0100
commit9dcdc67253fbef058e292c68cf33faea55e0246d (patch)
tree388cf009ed5547d6c97c5da7bb7b584cceb2a8dd
parentb9028758d85f8752f94b388c569847e5aa4ce21b (diff)
screen: a batch Automerge refuses names its changes
Review section 7: Automerge takes the changes judged in a batch all or none, so one it won't take (a fork of an actor's changes, under a policy without signatures) fails the whole batch, and every batch carrying it. The log said who sent the message (`Failed to process sync message`, `Delta download failed`, with the peer) but not what failed. The error now names the changes it was given, the first eight, each with its seq and the node it says made it, so whoever sends the offending one shows in the log beside what it sent.
-rw-r--r--src/access/screen.rs43
1 files changed, 41 insertions, 2 deletions
diff --git a/src/access/screen.rs b/src/access/screen.rs
index aa16b42..ae61416 100644
--- a/src/access/screen.rs
+++ b/src/access/screen.rs
@@ -433,7 +433,7 @@ pub(crate) fn apply(
if relevant(&paths[index]) || reaches_signed(doc, &paths[index]) {
// What rules read changed: in before the next is judged.
screened.applied += accepted.len();
- doc.apply_changes_log_patches(std::mem::take(&mut accepted), log)?;
+ apply_taken(doc, std::mem::take(&mut accepted), log)?;
frontier.clear();
} else {
frontier.insert(hash, seen_at);
@@ -460,7 +460,7 @@ pub(crate) fn apply(
}
screened.applied += accepted.len();
if !accepted.is_empty() {
- doc.apply_changes_log_patches(accepted, log)?;
+ apply_taken(doc, accepted, log)?;
}
for (hash, root) in refused {
refusals.insert(hash, root);
@@ -473,6 +473,45 @@ pub(crate) fn apply(
Ok(screened)
}
+/// `accepted` applied to `doc`. Automerge takes them all or none: one it won't take (a
+/// fork of an actor's changes, under a policy without signatures) fails the lot, so the
+/// error names them (the first few), with the node each says made it, for the log of
+/// whoever brought them.
+fn apply_taken(
+ doc: &mut Document,
+ accepted: Vec<Change>,
+ log: &mut crate::document::PatchLog,
+) -> Result<()> {
+ const NAMED: usize = 8;
+ let count = accepted.len();
+ let mut named: Vec<String> = accepted
+ .iter()
+ .take(NAMED)
+ .map(|change| {
+ let author = change
+ .author()
+ .and_then(|author| {
+ std::str::from_utf8(author.as_bytes())
+ .ok()
+ .map(str::to_owned)
+ })
+ .unwrap_or_else(|| "its actor's node".to_owned());
+ format!("{} (seq {} by {author})", change.hash(), change.seq())
+ })
+ .collect();
+ if count > NAMED {
+ named.push(format!("{} more", count - NAMED));
+ }
+ doc.apply_changes_log_patches(accepted, log)
+ .map_err(|error| {
+ anyhow::anyhow!(
+ "Automerge took none of the {count} changes judged: {error} ({})",
+ named.join(", ")
+ )
+ })?;
+ Ok(())
+}
+
/// What `apply_bytes` did.
#[derive(Debug, Default, Clone)]
pub(crate) struct Outcome {