0c3291310c79

report deleted damaged remote copies when a Doctor resend fails part way

time
2026-10-07 17:41 UTC · 5 days ago
machine
Junes-MacBook-Pro.local
agent
claude-opus-5-5
session
resolve doctor safety findings
parent
573800eb25d3
3 files src/exchange/exchange.v+5 −4src/exchange/exchange_test.v+27 −0src/exchange/repair.v+1 −0
src/exchange/exchange.v+5 −4
@@ -180,14 +180,15 @@180180 mut:181181 	saves   []string182182 	objects map[string]bool183+	deleted int // damaged remote copies removed before their replacement183184 }184185 185186 fn (u Uploaded) done() []string {186-	return if u.saves.len + u.objects.len == 0 {187-		[]string{}188-	} else {189-		['uploaded ${fmt.plural(u.objects.len, 'object')} and ${fmt.plural(u.saves.len, 'save record')}']187+	mut done := done_text('deleted', u.deleted, 'damaged remote object')188+	if u.saves.len + u.objects.len > 0 {189+		done << 'uploaded ${fmt.plural(u.objects.len, 'object')} and ${fmt.plural(u.saves.len, 'save record')}'190190 	}191+	return done191192 }192193 193194 // upload_line sends every save after the remote head, then the events.

view file

src/exchange/exchange_test.v+27 −0
@@ -1530,6 +1530,33 @@15301530 	assert unsettled(a)!.len == 315311531 }15321532 1533+// Deleting a damaged remote copy is work done, even when its replacement fails.1534+fn test_a_resend_reports_a_damaged_copy_it_deleted() {1535+	base := scratch('resend_deleted')!1536+	defer {1537+		os.rmdir_all(base) or {}1538+	}1539+	remote_dir := os.join_path(base, 'remote')1540+	mut backend := remote.new_folder(remote_dir)!1541+	mut a := open_repo(machine('resend_deleted_a')!)!1542+	defer {1543+		a.close()1544+	}1545+	put(a.root, 'app', 'app\n')!1546+	make_save(a, 'app')!1547+	hash := storage.hash_hex('app\n'.bytes())1548+	backend.put_if_absent(object_key('proj', hash), 'damaged'.bytes())!1549+	mut flaky := FaultyBackend{1550+		inner:      remote.new_folder(remote_dir)!1551+		fail_write: 11552+	}1553+	if _ := resend(a, mut flaky, 'proj', [hash], []) {1554+		assert false, 'the injected write failure should have surfaced'1555+	} else {1556+		assert (err as Partway).done == 'deleted 1 damaged remote object', (err as Partway).done1557+	}1558+}1559+15331560 // A strip that sent its event before failing says so.15341561 fn test_a_strip_that_fails_after_its_event_reports_it() {15351562 	base := scratch('strip_event_partway')!

view file

src/exchange/repair.v+1 −0
@@ -111,6 +111,7 @@111111 				}112112 			}113113 			backend.delete(key)!114+			uploaded.deleted++114115 		}115116 		store.get_object(hash)!116117 		if backend.put_if_absent(key, store.object_blob(hash)!)! {

view file