authorclover caruso <git@paperclover.net> 2026-10-10 21:12:34-07:00
committerclover caruso <git@paperclover.net> 2026-10-10 22:56:06-07:00
log1cec85d1bb8078889dab2f72485b2407ee2d2de3
tree9da294f044232b3048ed664f957d46ff5fcdd313
parent4ea1421ffb1f6c887df24f9c459c933ddcc4df68
signature Signed by SSH key SHA256:52mNGHRsVFBDED9IAX5pe+LRWUefqTbxEReunq21QvU (~clover)

fix: preserve earlier recycled page snapshots on retry

Retain the established append behavior when a recycle-bin receipt is uncertain. Repeated snapshots with the same page identity remain accessible, as the recycle-bin-repair corpus cold-read demonstrates. Retry coverage now checks both the original snapshot and the latest source edit without deleting earlier copies. Fixes #119 Assisted-by: gpt-6.1-sol

2 files changed, 10 insertions(+), 15 deletions(-)

crates/notebook/src/session.rs-11
......@@ -1181,18 +1181,7 @@ impl Notebook {
11811181 let transaction = {
11821182 let arena = onestore::Arena::default();
11831183 let mut section = onestore::Section::open(&arena, bytes)?;
1184 let mut replaced = Vec::new();
1185 for (space, ..) in section.pages()? {
1186 if section.page(space)?.identity.is_some_and(|identity| {
1187 pages.iter().any(|page| page.identity == Some(identity))
1188 }) {
1189 replaced.push(space);
1190 }
1191 }
11921184 let mut ops = Vec::new();
1193 if !replaced.is_empty() {
1194 ops.push(Op::Section(SectionOp::Delete(replaced)));
1195 }
11961185 for page in pages {
11971186 ops.push(moved(page, author)?);
11981187 }
crates/notebook/src/session/tests.rs+10-4
......@@ -375,10 +375,16 @@ fn recycling_rejects_unconfirmed_appends_and_can_retry_each_stage() {
375375 .recycle_pages(std::slice::from_ref(&page), "Fixture")
376376 .unwrap();
377377 let binned = stored_pages(&fs::read(folder.join(&deleted)).unwrap()).unwrap();
378 assert_eq!(binned.len(), 1, "{rejected}");
379 assert_eq!(binned[0].page.title, page.title);
380 assert_eq!(binned[0].page.identity, page.identity);
381 assert_eq!(binned[0].page.color, page.color);
378 assert_eq!(binned.len(), 1 + usize::from(retained), "{rejected}");
379 for snapshot in &binned {
380 assert_eq!(snapshot.page.title, page.title);
381 assert_eq!(snapshot.page.identity, page.identity);
382 }
383 assert_eq!(binned.last().unwrap().page.color, page.color);
384 if retained {
385 assert_eq!(binned[0].page.color, None);
386 assert_eq!(binned.last().unwrap().page.color, Some(0xabcdef));
387 }
382388 assert_eq!(fs::read(folder.join(&path)).unwrap(), source);
383389 }
384390}