Avoid copying the saved note during title sync (#570) #574

Closed
kayg wants to merge 0 commits from issue/570-save-copy into dev
Owner

Saving a note with thousands of independent writers rebuilt the saved CRDT on every title sync. The room now remembers the saved H1 and frontmatter title, initializes them from the recovered base on load, and refreshes them when a save or outside merge changes that base. Title precedence and the pending-save, file-replacement and promotion order are preserved.

Tests compare saved bytes and titles with the old path over 800 seeded cases, including Unicode, conflicting titles, outside edits, reloads, CRLF and BOM. Real SIGKILL tests stop before and after file replacement and verify recovery. Another test sends updates from three typists during a paused save and checks client, file and history agreement. A regression test proves title sync does not decode the saved document.

Release save latency, p50 / p95 in milliseconds, 50 samples per size; each appended log line has a fresh writer. Only the save is timed. Automatic saves, watching and automatic renames are disabled.

Lines Local before Local after Perf VM before Perf VM after
100 0.66 / 0.86 0.59 / 3.72 24.23 / 39.00 20.90 / 26.86
1,000 3.28 / 3.97 2.59 / 7.76 24.94 / 36.61 27.76 / 48.50
3,500 15.49 / 21.01 3.30 / 5.60 33.44 / 55.45 20.83 / 38.97

The VM runs were consecutive under one perf VM lock. Storage and shared-host scheduling cause substantial variation: the 1,000-line VM after run included a 236.46 ms outlier, and small-note local p95 rose. The 3,500-line improvement appears on both machines; these measurements do not establish an improvement at every size. The new save_bench example reproduces the workload.

Validation used the required local build slot, cargo -j4, and CARGO_TARGET_DIR=/home/kayg/Developer/calternal-target-570. Explicit installed Nix GCC and OpenSSL paths resolved initial tool discovery failures.

cargo fmt --all --check: exit 0
cargo clippy --workspace --all-targets -- -D warnings: exit 0
cargo test --workspace: exit 0

Workspace tests: 496 passed, 0 failed, 2 ignored. Evidence, including the original baseline, the failing old-path regression and full result summaries, is in issue #570. No evidence files are committed.

Refs #570. Follow-up to #527 / #529.

Saving a note with thousands of independent writers rebuilt the saved CRDT on every title sync. The room now remembers the saved H1 and frontmatter title, initializes them from the recovered base on load, and refreshes them when a save or outside merge changes that base. Title precedence and the pending-save, file-replacement and promotion order are preserved. Tests compare saved bytes and titles with the old path over 800 seeded cases, including Unicode, conflicting titles, outside edits, reloads, CRLF and BOM. Real SIGKILL tests stop before and after file replacement and verify recovery. Another test sends updates from three typists during a paused save and checks client, file and history agreement. A regression test proves title sync does not decode the saved document. Release save latency, p50 / p95 in milliseconds, 50 samples per size; each appended log line has a fresh writer. Only the save is timed. Automatic saves, watching and automatic renames are disabled. | Lines | Local before | Local after | Perf VM before | Perf VM after | | --- | --- | --- | --- | --- | | 100 | 0.66 / 0.86 | 0.59 / 3.72 | 24.23 / 39.00 | 20.90 / 26.86 | | 1,000 | 3.28 / 3.97 | 2.59 / 7.76 | 24.94 / 36.61 | 27.76 / 48.50 | | 3,500 | 15.49 / 21.01 | 3.30 / 5.60 | 33.44 / 55.45 | 20.83 / 38.97 | The VM runs were consecutive under one perf VM lock. Storage and shared-host scheduling cause substantial variation: the 1,000-line VM after run included a 236.46 ms outlier, and small-note local p95 rose. The 3,500-line improvement appears on both machines; these measurements do not establish an improvement at every size. The new save_bench example reproduces the workload. Validation used the required local build slot, cargo -j4, and CARGO_TARGET_DIR=/home/kayg/Developer/calternal-target-570. Explicit installed Nix GCC and OpenSSL paths resolved initial tool discovery failures. ``` cargo fmt --all --check: exit 0 cargo clippy --workspace --all-targets -- -D warnings: exit 0 cargo test --workspace: exit 0 ``` Workspace tests: 496 passed, 0 failed, 2 ignored. Evidence, including the original baseline, the failing old-path regression and full result summaries, is in [issue #570](https://git.kayg.org/kayg/calternal/issues/570). No evidence files are committed. Refs #570. Follow-up to #527 / #529.
Avoid reconstructing saved notes to sync titles (#570)
Some checks failed
CI / web (pull_request) Failing after 9s
CI / server (pull_request) Successful in 24s
CI / rust (pull_request) Successful in 2m33s
b97d6b4909
Author
Owner

Independent defensive review started. Review worktree is detached at b97d6b4909 (origin/issue/570-save-copy), comparing against origin/dev b42e8b9819. Review only: no branch pushes, merges or deployment. I will add differential tests and run kill -9 save recovery checks. Review notes: /home/kayg/codex-jobs/review-574/review.md.

Independent defensive review started. Review worktree is detached at b97d6b490992fc24c1fa9eb73f11441c14b9c57e (origin/issue/570-save-copy), comparing against origin/dev b42e8b98192c2b30b8a4b0188a01c58457d3bfa0. Review only: no branch pushes, merges or deployment. I will add differential tests and run kill -9 save recovery checks. Review notes: /home/kayg/codex-jobs/review-574/review.md.
Author
Owner

Independent review progress: all production saved-base assignments were traced (load, outside merge, successful save). Each corresponding title-cache update uses that exact base. The actor serializes mutations across the save awaits. yrs 0.28.0 GetString ignores formatting markers and deleted items; state encoding includes pending updates, so extra differential tests exercise pending cross-writer dependencies, format marks, root-map changes, deletes, empty/whitespace notes, Unicode, outside edits, restored text, tag patches and fresh Notes/Home restarts. Tests compare file bytes, CRDT state, title-history update bytes and versions with the old reconstruction path. Initial compilation passed; the next run is queued behind the shared build slots. No correctness finding yet.

Independent review progress: all production saved-base assignments were traced (load, outside merge, successful save). Each corresponding title-cache update uses that exact base. The actor serializes mutations across the save awaits. yrs 0.28.0 GetString ignores formatting markers and deleted items; state encoding includes pending updates, so extra differential tests exercise pending cross-writer dependencies, format marks, root-map changes, deletes, empty/whitespace notes, Unicode, outside edits, restored text, tag patches and fresh Notes/Home restarts. Tests compare file bytes, CRDT state, title-history update bytes and versions with the old reconstruction path. Initial compilation passed; the next run is queued behind the shared build slots. No correctness finding yet.
Author
Owner

Review test update: 10 focused tests passed, including SIGKILL before/after replacement, SIGKILL after the save reply, the 196-pair title matrix, pending deletes, live trailing formatting markers and failure retries. The fuzz run stopped in test setup while opening notes/meta.db (unable to open database file), without a differential mismatch. This process has a 1024-descriptor limit; the first harness accumulated fresh server background tasks across all 24 seeds. I changed the review harness to tear down its runtime between seeds and queued the full notes/fsio tests and notes clippy. This is a review-harness issue, not a demonstrated PR regression.

Review test update: 10 focused tests passed, including SIGKILL before/after replacement, SIGKILL after the save reply, the 196-pair title matrix, pending deletes, live trailing formatting markers and failure retries. The fuzz run stopped in test setup while opening notes/meta.db (unable to open database file), without a differential mismatch. This process has a 1024-descriptor limit; the first harness accumulated fresh server background tasks across all 24 seeds. I changed the review harness to tear down its runtime between seeds and queued the full notes/fsio tests and notes clippy. This is a review-harness issue, not a demonstrated PR regression.
Author
Owner

Independent correctness review of PR #574: SAFE TO MERGE

Reviewed head b97d6b490992fc24c1fa9eb73f11441c14b9c57e (origin/issue/570-save-copy), detached review worktree, against origin/dev b42e8b98192c2b30b8a4b0188a01c58457d3bfa0. The comparison base is also the merge base. The PR head was checked again before posting.

No correctness regression found. No required changes.

The change caches exactly the two strings the old save path extracted from the saved CRDT. It does not construct different Yjs updates or change item origins, unlike the append optimization in #529.

I traced every production assignment to Room.base_state:

  • Load initializes the cache after pending-save recovery and outside-file reconciliation, from reconstructed saved state, rather than unsaved live history.
  • Outside merge refreshes it from the private base fork, rather than the merged live document.
  • Successful save refreshes it from the text captured with the saved state. The actor handles one command at a time, so typing cannot mutate the document across the save awaits. There is no await between assigning the cache and assigning its corresponding saved state.
  • A save with identical file bytes leaves the saved base alone. Formatting, root-map changes and deletes that leave the saved title strings unchanged do not require refreshing that projection. Version restores and tag patches change the live document; they correctly leave the saved comparison base alone until a save.

I read yrs 0.28.0 types/text.rs (GetString), transaction.rs (full-state encoding, delete sets and pending-update integration), plus the save, load, merge, actor, history and file replacement paths. Text extraction includes live string items and ignores formatting markers and deleted items. The title parser and precedence conditions are unchanged.

Added review tests in crates/notes/src/room/save/review.rs, wired through save/tests.rs. They remain in this worktree; no commits or pushes were made.

  • 2,424 differential saves, 24 seeds, 264 fresh Notes/Home restarts. Random whole and partial deletes, empty and whitespace notes, emoji and ZWJ sequences, H1-only and frontmatter-title-only changes, conflicting titles, formatting, root-map insertion/removal, cross-writer dependent updates delivered out of order and retried, outside edits with CRLF/BOM, version-based replacement and tag-renaming patches. Restarts occur with both saved and unsaved live history. Every save compares against the old reconstruction path: exact file bytes, full encoded CRDT state, title-history update bytes, resulting version and history replay text. Cache values are also checked against the reconstructed saved base after mutations and recovery.
  • 196 saved-base/live-text pairs. Missing, empty, conflicting, duplicate and malformed frontmatter titles, fenced H1s, blank headings, whitespace and Unicode.
  • Directed pending deletes and formatting. Delete set arrives before its insert, then save, reload, insert and retry. A live closing formatting marker trails the text before a subsequent title-only save. All differential checks pass.
  • Failure and retry paths. WriteFails, ErrorAfterWrite, an outside edit missed by the watcher and discovered during save, then a title-only edit after the save retry. Saved-base cache remains consistent.
  • Actual SIGKILL. The PR's existing tests kill before file replacement and after replacement but before promotion. My additional test kills immediately after the actual Notes::save reply. On restart, its exact acknowledged file hash, text and version survive; the recovered cache and following title-only save agree with the old path. The added test verifies termination signal 9.
  • Concurrent typists. The PR's paused-save test passed with three real client sessions, checking file, clients and history. The full notes suite also passed, including transport pending/resync, restore, tag, external-edit and save-failure coverage.

Crash durability ordering is unchanged: pending history is durable before replacement; the temporary file is fsynced; replacement fsyncs its directory; history promotion is awaited before save returns. Notes history retains WAL with synchronous=FULL. A physical power cut was not injected; the power-loss assessment is from this unchanged persistence sequence, supplemented by the process-kill tests.

Final gates: 238 passed, 0 failed, 1 pre-existing ignored performance test (index::tests::reload_speed). Formatting and notes clippy passed. All local builds/tests/clippy used the required build-slot wrapper, cargo -j4, and CARGO_TARGET_DIR=/home/kayg/Developer/calternal-target-574r.

Review harness note: an initial compile needed the yrs observer's subscription key. The first fuzz harness then accumulated server background tasks across all seeds and stopped opening a temporary SQLite database under the process's 1,024-descriptor limit. Each seed now tears down its runtime. The complete rerun below passed; neither setup failure was an old/new differential failure.

Full final gate output, verbatim:

GATE: cargo fmt --all --check
GATE EXIT: 0
GATE: cargo test -j4 -p calternal-notes -p calternal-fsio --lib -- --test-threads=4 --nocapture
   Compiling calternal-fsio v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/fsio)
   Compiling calternal-notes v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/notes)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 5.64s
     Running unittests src/lib.rs (/home/kayg/Developer/calternal-target-574r/debug/deps/calternal_fsio-a183b1a7bb6b5526)

running 12 tests
test tests::atomic_write_replaces_and_leaves_no_temp_files ... ok
test beneath::tests::walking_without_openat2_refuses_symlinks_too ... ok
test beneath::tests::temp_files_persist_without_replacing_and_vanish_when_dropped ... ok
test beneath::tests::paths_that_climb_out_are_refused ... ok
test tests::case_only_rename_works_and_other_clashes_are_refused ... ok
test tests::names_fold_case_and_unicode_form ... ok
test tests::failed_write_keeps_the_old_file ... ok
test tests::only_one_process_owns_a_data_folder ... ok
test tests::unique_names_skip_case_and_form_clashes ... ok
test tests::prepared_writes_land_only_when_placed ... ok
test tests::write_unless_changed_keeps_someone_elses_version ... ok
test tests::big_writes_arrive_whole ... ok

test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s

     Running unittests src/lib.rs (/home/kayg/Developer/calternal-target-574r/debug/deps/calternal_notes-6ac741039e501ea4)

running 227 tests
test anchor::tests::lines_by_utf16_index ... ok
test backlinks::tests::snippets_are_the_trimmed_line ... ok
test anchor::tests::hash_is_plain_sha256_hex ... ok
test backlinks::tests::links_are_fold_keys_and_lines ... ok
test blocks::tests::finds_list_children_and_heading_sections ... ok
test blocks::tests::line_edits ... ok
test blocks::tests::shapes_keep_markers ... ok
test blocks::tests::titles_from_the_first_sentence ... ok
test bulk::undo::tests::inverse_restores_the_text ... ok
test diff::tests::an_inserted_line_starts_at_a_line_start ... ok
test diff::tests::emoji_positions_are_utf16 ... ok
test diff::tests::small_change_is_a_small_edit ... ok
test frontmatter::tests::h1_skips_code_and_frontmatter ... ok
test frontmatter::tests::quoting ... ok
test frontmatter::tests::reads_keys ... ok
test frontmatter::tests::set_adds_a_line_or_a_block ... ok
test frontmatter::tests::set_changes_only_the_value ... ok
test frontmatter::tests::set_is_utf16_aware ... ok
test history::tests::appends_are_ordered_and_readable ... ok
test history::tests::pending_save_promotes_to_base ... ok
test index::tests::a_shared_path_answers_with_the_newest_note ... ok
test index::tests::a_torn_tail_cannot_swallow_the_next_durable_mapping ... ok
test bulk::undo::tests::inverse_roundtrips ... ok
test diff::tests::edits_turn_old_into_new ... ok
test index::tests::compacts_when_it_grows ... ok
test index::tests::reload_speed ... ignored
test index::tests::survives_reload_renames_and_torn_lines ... ok
test diff::tests::edits_never_split_a_character ... ok
test index::tests::under_finds_exact_paths_and_descendants_with_folded_names ... ok
test index::tests::under_keeps_shadowed_paths_for_watcher_removal ... ok
test links::tests::finds_standard_and_old_style_links ... ok
test index::tests::unloads_and_reloads ... ok
test links::tests::nested_brackets_in_text ... ok
test links::tests::relative_paths ... ok
test links::tests::resolves_relative_to_the_linking_note ... ok
test links::tests::retarget_follows_the_title_and_keeps_custom_text ... ok
test links::tests::retarget_from_a_subfolder ... ok
test links::tests::skips_code_images_and_the_web ... ok
test links::tests::retarget_matches_case_and_unicode_forms ... ok
test meta::tests::cursors_round_trip_and_reject_junk ... ok
test meta::tests::snippets_skip_frontmatter_and_headings ... ok
test missing::targets::tests::a_name_is_the_file_without_its_folder_and_extension ... ok
test missing::targets::tests::names_rank_exact_then_prefix_then_word_then_inside ... ok
test rename::tests::clash_numbers ... ok
test rename::tests::daily_names ... ok
test rename::tests::h1_is_set_or_added ... ok
test room::acks::tests::varuints_round_trip ... ok
test room::edit::tests::checks_bounds_overlap_and_surrogates ... ok
test room::save::tests::concurrent_typists_and_saves_converge ... ok
test room::save::tests::review::review_acknowledged_save_child ... ok
test room::save::tests::kill_nine_mid_save_recovers_title_and_bytes ... ok
REVIEW574 pending delete before insert, reload, deleted heading and live trailing format markers agree
test room::save::tests::review::review_delete_set_before_insert_and_trailing_format_marks ... ok
REVIEW574 failure retry: WriteFails, ErrorAfterWrite, save-time external merge and following title-only save agree
test room::save::tests::review::review_failed_save_retry_and_outside_retry_keep_base_cache ... ok
REVIEW574 SIGKILL after Notes::save reply: exact file hash, text, version, recovered cache and next title-only save agree
test room::save::tests::review::review_kill_nine_after_save_reply_preserves_exact_file_history_and_version ... ok
test index::tests::directory_counts_match_full_scan_after_moves_and_reload ... ok
test room::save::tests::save_kill_child ... ok
test index::tests::answers_like_plain_maps ... ok
test room::save::tests::title_sync_does_not_decode_the_saved_document ... ok
test scan::tests::day_ids_name_their_files ... ok
test scan::tests::state_round_trips_and_skips_odd_names ... ok
test search::tests::a_note_becomes_a_document ... ok
test search::tests::plain_search_lines_borrow_their_text ... ok
test search::tests::search_lines_keep_link_text_and_replacement_order ... ok
test search::tests::titles_fall_back_to_the_file_name ... ok
test smartline::tests::a_plain_line ... ok
test smartline::tests::only_tags_are_words ... ok
test smartline::tests::trailing_tags_only ... ok
test stamp::tests::formats_with_offset ... ok
test tags::front::tests::every_way_of_writing_tags ... ok
test tags::merge::tests::property_tags_go_with_their_comma_or_line ... ok
test tags::merge::tests::text_tags_go_with_one_space ... ok
test tags::rewrite::tests::merging_keeps_every_tag_once_at_most ... ok
test tags::rewrite::tests::nothing_to_do_is_no_edits ... ok
test tags::rewrite::tests::only_real_tags_change ... ok
test tags::rewrite::tests::positions_count_utf16 ... ok
test tags::rewrite::tests::properties_change_name_only ... ok
test tags::rewrite::tests::rename_and_back_is_the_same_text ... ok
test tags::rewrite::tests::renames_tags_and_nested_tags_only ... ok
test tags::rewrite::tests::renaming_onto_an_existing_tag_merges ... ok
test tags::rewrite::tests::words_code_links_and_headings ... ok
test tags::scan::tests::code_is_never_tags ... ok
test tags::scan::tests::hash_free_lines_still_open_and_close_code_fences ... ok
test tags::scan::tests::headings_and_quotes_keep_their_tags ... ok
test tags::scan::tests::link_targets_and_addresses_are_never_tags ... ok
test tags::scan::tests::not_tags ... ok
test tags::scan::tests::plain_tags ... ok
test tags::tests::nesting_and_cleaning ... ok
test tags::tests::properties_and_text ... ok
test tests::acks::an_older_lineage_is_refused_before_anything_is_sent ... ok
test tests::acks::checks_are_answered_in_order ... ok
test tests::acks::lineage_survives_a_restart ... ok
test tests::acks::saved_means_the_change_is_in_history ... ok
test tests::anchors::an_anchor_follows_its_block_when_lines_shift ... ok
test tests::anchors::an_edited_block_is_refused_never_the_wrong_one ... ok
test tests::anchors::anchors_need_a_hash_and_block_ids_work ... ok
test tests::anchors::history_folds_typing_and_names_jobs ... ok
REVIEW574 completed seed 1
test tests::anchors::shifting_lines_never_move_the_wrong_block ... ok
test tests::appends::an_append_to_a_long_note_stays_small ... ok
REVIEW574 completed seed 2
test tests::appends::appends_built_directly_match_the_copy_byte_for_byte ... ok
REVIEW574 completed seed 3
test tests::appends::typists_and_appends_interleave_on_one_note ... ok
test tests::appends_review::delete_whole_tail_then_append_matches ... ok
test tests::appends_review::frontmatter_map_write_falls_back ... ok
test room::save::tests::saved_bytes_and_titles_match_the_old_copy_path ... ok
A2 old=[1, 1, 233, 7, 0, 196, 232, 7, 2, 9, 11, 3, 65, 50, 10, 0]
A2 new=[1, 1, 233, 7, 0, 196, 232, 7, 2, 9, 11, 3, 65, 50, 10, 0]
byte-identical=true
test tests::appends_review::trailing_format_marker_bytes ... ok
test tests::fanout::a_crash_before_the_seed_is_durable_never_doubles_text ... ok
REVIEW574 completed seed 4
test tests::fanout::a_crash_between_fan_out_and_disk_loses_nothing ... ok
test tests::fanout::a_first_open_does_not_wait_for_the_disk ... ok
REVIEW574 completed seed 5
test tests::fanout::a_version_always_matches_its_text ... ok
test tests::fanout::bursts_converge_and_reach_history ... ok
REVIEW574 title matrix: 196 base/live pairs, including absent/empty/conflicting/duplicate/malformed titles, hidden H1s, whitespace and Unicode
test room::save::tests::review::review_title_precedence_edge_matrix ... ok
test tests::fresh::a_note_made_with_text_opens_with_its_frontmatter ... ok
test tests::kill9::child ... ok
test tests::kill9::delete_killed_after_its_answer_finishes_once ... ok
test tests::kill9::permanent_delete_after_restore_cannot_delete_the_live_note ... ok
test tests::kill9::quick_delete_restore_survives_kill ... ok
test tests::kill9::quick_restore_delete_survives_kill ... ok
test tests::kill9::restore_delete_forget_does_not_resurrect_after_kill ... ok
test tests::kill9::restore_killed_after_its_answer_finishes_once ... ok
test tests::fanout::collaborators_see_typing_before_the_disk ... ok
test tests::listing::filters_by_tag_kind_and_created_range ... ok
test tests::listing::pages_cover_every_note_once_in_a_stable_order ... ok
REVIEW574 completed seed 6
test tests::listing::pinning_shows_in_the_list_and_unpinning_leaves_no_trace ... ok
test tests::listing::the_first_page_never_waits_for_the_home_to_open ... ok
test tests::listing::a_common_tag_pages_the_same_in_every_order ... ok
REVIEW574 completed seed 7
test tests::listing::the_list_stays_live_with_new_notes_saves_and_outside_edits ... ok
fast appends: 4003
test tests::appends_review::fuzz_deletes_restarts_parked ... ok
test tests::missing::a_path_tells_here_trash_and_gone ... ok
test tests::missing::a_rename_in_the_app_leaves_the_old_path_moved ... ok
test tests::missing::make_from_link_makes_the_note_where_the_link_points_and_undo_takes_it_back ... ok
test tests::missing::never_made_targets_are_offered_with_their_capitals_and_link_counts ... ok
test tests::missing::never_written_paths_are_new_and_bad_ones_too ... ok
test tests::missing::notes_in_the_trash_from_before_are_found ... ok
test tests::missing::restored_with_a_new_name_reads_as_moved ... ok
test tests::missing::a_move_outside_is_found_and_relink_repoints_the_links ... ok
test tests::opening::a_cold_fill_ends_with_every_index_and_right_answers ... ok
test tests::opening::a_note_never_typed_in_is_still_forgotten_on_restart ... ok
test tests::opening::a_warm_open_announces_only_what_changed_while_closed ... ok
test tests::opening::a_cold_home_indexes_every_file_once_with_its_own_id ... ok
test tests::opening::identifying_an_already_indexed_file_never_makes_it_a_copy ... ok
test tests::opening::shutting_down_leaves_the_list_log_empty ... ok
test tests::opening::typing_saved_only_in_history_survives_a_restart_before_the_first_save ... ok
test tests::listing::the_kept_count_follows_adds_and_deletes ... ok
test tests::opening::upgrading_without_a_saved_scan_announces_nothing_for_known_notes ... ok
REVIEW574 completed seed 8
test tests::outside::edit_while_closed_is_merged_on_open ... ok
test tests::outside::missed_watcher_event_is_caught_before_saving ... ok
test tests::outside::copied_note_gets_a_fresh_id ... ok
test tests::opening::asking_mid_scan_never_says_not_found_and_never_duplicates_a_day ... ok
test tests::outside::outside_create_and_delete ... ok
test tests::outside::outside_edit_while_typing_merges ... ok
REVIEW574 completed seed 9
test tests::outside::outside_rename_keeps_the_id ... ok
test tests::peek::an_open_room_answers_with_its_live_text ... ok
test tests::peek::reads_files_without_opening_rooms ... ok
test tests::peek::returns_at_most_fifty ... ok
test tests::pending::apply_places_changes_in_any_order ... ok
test tests::pending::changes_out_of_order_all_arrive_and_saved_is_true ... ok
test tests::pending::yrs_alone_loses_a_parked_change ... ok
REVIEW574 completed seed 10
test tests::outside::own_saves_are_not_outside_edits ... ok
test tests::renames::a_taken_name_gets_a_number_and_daily_notes_keep_theirs ... ok
test tests::outside::polling_finds_outside_changes ... ok
test tests::renames::backlinks_list_linked_and_unlinked_mentions ... ok
test tests::renames::make_note_moves_the_block_and_undoes_as_one_step ... ok
test tests::renames::move_block_to_another_note_and_back ... ok
test tests::renames::rename_moves_the_file_and_repoints_every_link ... ok
REVIEW574 completed seed 11
test tests::renames::typing_a_new_title_renames_the_file_after_a_pause ... ok
test tests::renames::backlinks_follow_edits_and_deletes ... ok
test tests::renames::undo_keeps_a_made_note_someone_typed_in ... ok
test tests::renames::typing_in_a_linking_note_during_a_rename_is_kept ... ok
test tests::rooms::late_joiner_gets_full_state ... ok
test tests::rooms::patches_use_utf16_positions ... ok
test tests::rooms::fresh_note_opened_by_many_at_once_is_not_doubled ... ok
test tests::rooms::retried_change_applies_once ... ok
test tests::rooms::retried_create_makes_one_note ... ok
test tests::rooms::stale_replace_keeps_newer_typing ... ok
test tests::rooms::typing_survives_a_concurrent_delete ... ok
test tests::saving::a_write_error_keeps_the_change ... ok
REVIEW574 completed seed 12
test tests::rooms::random_concurrent_editing_converges_without_duplicates ... ok
test tests::saving::an_error_after_the_write_does_not_double_text ... ok
test tests::saving::crash_after_write_does_not_double_text ... ok
test tests::saving::failed_save_keeps_the_change ... ok
REVIEW574 completed seed 13
test tests::saving::first_keystroke_creates_the_file ... ok
test tests::saving::line_endings_and_bom_are_kept ... ok
test tests::saving::crash_before_write_loses_nothing ... ok
REVIEW574 completed seed 14
test tests::saving::opening_never_writes ... ok
REVIEW574 completed seed 15
test tests::saving::saving_does_not_loop ... ok
test tests::saving::title_and_h1_stay_in_step ... ok
REVIEW574 completed seed 16
test tests::saving::nonstop_typing_still_saves ... ok
test tests::renames::a_crash_mid_rename_resumes_and_applies_each_edit_once ... ok
REVIEW574 completed seed 17
test tests::storm::outside_rewrites_during_a_storm_all_land ... ok
test tests::tags::bad_names_are_refused ... ok
test tests::tags::renames_every_tag_and_undoes_as_one_step ... ok
test tests::tags::typing_during_a_tag_rename_is_kept ... ok
test tests::trash::a_closed_note_is_trashed_without_opening_it ... ok
test tests::tags::a_big_tag_rename_never_stalls_typing ... ok
test tests::trash::a_file_deleted_outside_goes_to_the_trash ... ok
test tests::trash::a_never_typed_note_is_simply_forgotten ... ok
test tests::trash::a_note_deleted_before_its_first_save_keeps_its_text ... ok
test tests::trash::delete_and_restore_answer_once_per_job_id ... ok
test tests::trash::delete_moves_to_trash_and_undo_brings_it_back ... ok
test tests::trash::delete_replay_with_a_stale_index_keeps_the_only_file ... ok
test tests::trash::folder_rename_and_outside_move_bursts_keep_every_descendant ... ok
test tests::trash::forget_deletes_file_and_history_forever ... ok
test tests::trash::half_written_trash_folders_never_hide_a_live_note ... ok
test tests::trash::restore_durably_indexes_a_note_without_an_embedded_id ... ok
test tests::trash::restore_keeps_id_and_history_and_takes_a_free_name ... ok
test tests::trash::restore_with_a_stale_index_really_moves_the_file_back ... ok
test tests::trash::the_sweep_empties_only_notes_older_than_thirty_days ... ok
test tests::trash::trash_ids_never_reach_another_home ... ok
REVIEW574 completed seed 18
test tests::trash::trash_pages_newest_first ... ok
test tests::trash::trashed_notes_leave_search_lists_and_backlinks_and_come_back ... ok
REVIEW574 completed seed 19
test tests::unloading::a_connected_client_keeps_it ... ok
REVIEW574 completed seed 20
test tests::unloading::an_open_note_keeps_it ... ok
test tests::unloading::an_unused_home_unloads_and_answers_after ... ok
REVIEW574 completed seed 21
test tests::writers::every_writer_at_once_loses_and_doubles_nothing ... ok
test tests::writers::every_writer_at_once_with_the_watcher ... ok
test text::tests::mixed_endings_take_the_majority ... ok
test text::tests::styles_round_trip ... ok
test text::tests::uniform_files_round_trip_exactly ... ok
test trash::tests::a_note_without_details_still_reads ... ok
test trash::tests::details_without_a_note_are_not_a_note ... ok
test trash::tests::missing_details_never_age_trash_from_the_notes_old_mtime ... ok
test versions::tests::folds_runs_by_author_kind_and_gap ... ok
test ydoc::tests::fork_and_merge_keeps_concurrent_typing ... ok
test ydoc::tests::reapplying_an_update_changes_nothing ... ok
test ydoc::tests::utf16_offsets ... ok
REVIEW574 completed seed 22
REVIEW574 completed seed 23
test tests::trash::a_crash_mid_delete_finishes_on_restart_exactly_once ... ok
REVIEW574 completed seed 24
REVIEW574 fuzz operation counts: [177, 198, 178, 166, 183, 173, 164, 181, 154, 170, 166, 180, 162, 148]
REVIEW574 differential: 2424 saves, 264 fresh Notes/Home restarts; file bytes, CRDT state, title-update bytes, history replay and versions agree
test room::save::tests::review::review_fuzz_old_new_bytes_history_versions_and_restarts ... ok
test tests::tags::a_crash_mid_rename_resumes_and_renames_each_tag_once ... ok

test result: ok. 226 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 13.39s

GATE EXIT: 0
GATE: cargo clippy -j4 -p calternal-notes --all-targets -- -D warnings
    Checking cfg-if v1.0.5
    Checking rand_core v0.10.1
    Checking libc v0.2.190
    Checking once_cell v1.21.4
    Checking bitflags v2.13.2
    Checking pin-project-lite v0.2.17
    Checking serde_core v1.0.229
    Checking smallvec v1.16.3
    Checking linux-raw-sys v0.12.1
    Checking typenum v1.20.1
    Checking rustix v1.1.5
    Checking getrandom v0.4.3
    Checking hybrid-array v0.4.15
    Checking zmij v1.0.23
    Checking parking v2.2.1
    Checking itoa v1.0.18
    Checking cpufeatures v0.3.1
    Checking memchr v2.8.3
    Checking fastrand v2.5.0
    Checking event-listener v5.4.2
    Checking tracing-core v0.1.36
    Checking foldhash v0.2.0
    Checking scopeguard v1.2.0
    Checking hashbrown v0.17.1
    Checking lock_api v0.4.14
    Checking serde v1.0.229
    Checking serde_json v1.0.154
    Checking tracing v0.1.44
    Checking uuid v1.28.0
    Checking thiserror v2.0.21
    Checking ref-cast v1.0.27
    Checking event-listener-strategy v0.5.4
    Checking inotify-sys v0.1.8
    Checking parking_lot_core v0.9.12
    Checking crossbeam-utils v0.8.23
    Checking crypto-common v0.2.2
    Checking block-buffer v0.12.1
    Checking hashbrown v0.14.5
    Checking tinyvec v1.13.3
    Checking dyn-clone v1.0.20
   Compiling time-core v0.1.9
    Checking log v0.4.34
    Checking same-file v1.0.6
    Checking const-oid v0.10.2
   Compiling num-conv v0.2.2
    Checking walkdir v2.5.0
    Checking dashmap v6.2.1
    Checking digest v0.11.3
   Compiling time-macros v0.2.32
    Checking mio v1.2.4
    Checking unicode-normalization v0.1.25
    Checking schemars v1.2.3
    Checking tokio v1.53.2
    Checking libsqlite3-sys v0.38.2
    Checking inotify v0.11.5
    Checking async-lock v3.4.2
    Checking arc-swap v1.9.2
    Checking hashlink v0.12.2
    Checking tempfile v3.27.0
    Checking wait-timeout v0.2.1
    Checking smallstr v0.3.1
    Checking notify-types v2.1.0
    Checking bit-vec v0.10.1
    Checking quick-error v1.2.3
    Checking arrayvec v0.7.8
    Checking fnv v1.0.7
    Checking fallible-streaming-iterator v0.1.9
    Checking fallible-iterator v0.3.0
    Checking powerfmt v0.2.1
    Checking deranged v0.5.9
    Checking constant_time_eq v0.4.2
    Checking blake3 v1.8.7
    Checking calternal-core v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/core)
    Checking rusqlite v0.40.2
    Checking rusty-fork v0.3.1
    Checking bit-set v0.11.1
    Checking notify v8.2.0
    Checking yrs v0.28.0
    Checking num-traits v0.2.19
    Checking calternal-fsio v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/fsio)
    Checking sha2 v0.11.1
    Checking chacha20 v0.10.2
    Checking rand v0.10.3
    Checking calternal-memory v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/memory)
    Checking rand_xorshift v0.5.0
    Checking regex-syntax v0.8.11
    Checking core_detect v1.0.0
    Checking base64 v0.22.1
    Checking time v0.3.55
    Checking hashbrown v0.16.1
    Checking unicode-segmentation v1.13.3
    Checking similar v3.2.0
    Checking unarray v0.1.4
    Checking calternal-testkit v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/testkit)
    Checking proptest v1.12.0
    Checking calternal-notes v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/notes)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 19.16s
GATE EXIT: 0
# Independent correctness review of PR #574: SAFE TO MERGE Reviewed head `b97d6b490992fc24c1fa9eb73f11441c14b9c57e` (`origin/issue/570-save-copy`), detached review worktree, against `origin/dev` `b42e8b98192c2b30b8a4b0188a01c58457d3bfa0`. The comparison base is also the merge base. The PR head was checked again before posting. No correctness regression found. No required changes. The change caches exactly the two strings the old save path extracted from the saved CRDT. It does not construct different Yjs updates or change item origins, unlike the append optimization in #529. I traced every production assignment to `Room.base_state`: - Load initializes the cache after pending-save recovery and outside-file reconciliation, from reconstructed saved state, rather than unsaved live history. - Outside merge refreshes it from the private base fork, rather than the merged live document. - Successful save refreshes it from the text captured with the saved state. The actor handles one command at a time, so typing cannot mutate the document across the save awaits. There is no await between assigning the cache and assigning its corresponding saved state. - A save with identical file bytes leaves the saved base alone. Formatting, root-map changes and deletes that leave the saved title strings unchanged do not require refreshing that projection. Version restores and tag patches change the live document; they correctly leave the saved comparison base alone until a save. I read yrs 0.28.0 `types/text.rs` (`GetString`), `transaction.rs` (full-state encoding, delete sets and pending-update integration), plus the save, load, merge, actor, history and file replacement paths. Text extraction includes live string items and ignores formatting markers and deleted items. The title parser and precedence conditions are unchanged. Added review tests in `crates/notes/src/room/save/review.rs`, wired through `save/tests.rs`. They remain in this worktree; no commits or pushes were made. - **2,424 differential saves, 24 seeds, 264 fresh Notes/Home restarts.** Random whole and partial deletes, empty and whitespace notes, emoji and ZWJ sequences, H1-only and frontmatter-title-only changes, conflicting titles, formatting, root-map insertion/removal, cross-writer dependent updates delivered out of order and retried, outside edits with CRLF/BOM, version-based replacement and tag-renaming patches. Restarts occur with both saved and unsaved live history. Every save compares against the old reconstruction path: exact file bytes, full encoded CRDT state, title-history update bytes, resulting version and history replay text. Cache values are also checked against the reconstructed saved base after mutations and recovery. - **196 saved-base/live-text pairs.** Missing, empty, conflicting, duplicate and malformed frontmatter titles, fenced H1s, blank headings, whitespace and Unicode. - **Directed pending deletes and formatting.** Delete set arrives before its insert, then save, reload, insert and retry. A live closing formatting marker trails the text before a subsequent title-only save. All differential checks pass. - **Failure and retry paths.** WriteFails, ErrorAfterWrite, an outside edit missed by the watcher and discovered during save, then a title-only edit after the save retry. Saved-base cache remains consistent. - **Actual SIGKILL.** The PR's existing tests kill before file replacement and after replacement but before promotion. My additional test kills immediately after the actual `Notes::save` reply. On restart, its exact acknowledged file hash, text and version survive; the recovered cache and following title-only save agree with the old path. The added test verifies termination signal 9. - **Concurrent typists.** The PR's paused-save test passed with three real client sessions, checking file, clients and history. The full notes suite also passed, including transport pending/resync, restore, tag, external-edit and save-failure coverage. Crash durability ordering is unchanged: pending history is durable before replacement; the temporary file is fsynced; replacement fsyncs its directory; history promotion is awaited before save returns. Notes history retains WAL with `synchronous=FULL`. A physical power cut was not injected; the power-loss assessment is from this unchanged persistence sequence, supplemented by the process-kill tests. Final gates: **238 passed, 0 failed, 1 pre-existing ignored performance test** (`index::tests::reload_speed`). Formatting and notes clippy passed. All local builds/tests/clippy used the required build-slot wrapper, cargo `-j4`, and `CARGO_TARGET_DIR=/home/kayg/Developer/calternal-target-574r`. Review harness note: an initial compile needed the yrs observer's subscription key. The first fuzz harness then accumulated server background tasks across all seeds and stopped opening a temporary SQLite database under the process's 1,024-descriptor limit. Each seed now tears down its runtime. The complete rerun below passed; neither setup failure was an old/new differential failure. Full final gate output, verbatim: ```text GATE: cargo fmt --all --check GATE EXIT: 0 GATE: cargo test -j4 -p calternal-notes -p calternal-fsio --lib -- --test-threads=4 --nocapture Compiling calternal-fsio v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/fsio) Compiling calternal-notes v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/notes) Finished `test` profile [unoptimized + debuginfo] target(s) in 5.64s Running unittests src/lib.rs (/home/kayg/Developer/calternal-target-574r/debug/deps/calternal_fsio-a183b1a7bb6b5526) running 12 tests test tests::atomic_write_replaces_and_leaves_no_temp_files ... ok test beneath::tests::walking_without_openat2_refuses_symlinks_too ... ok test beneath::tests::temp_files_persist_without_replacing_and_vanish_when_dropped ... ok test beneath::tests::paths_that_climb_out_are_refused ... ok test tests::case_only_rename_works_and_other_clashes_are_refused ... ok test tests::names_fold_case_and_unicode_form ... ok test tests::failed_write_keeps_the_old_file ... ok test tests::only_one_process_owns_a_data_folder ... ok test tests::unique_names_skip_case_and_form_clashes ... ok test tests::prepared_writes_land_only_when_placed ... ok test tests::write_unless_changed_keeps_someone_elses_version ... ok test tests::big_writes_arrive_whole ... ok test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s Running unittests src/lib.rs (/home/kayg/Developer/calternal-target-574r/debug/deps/calternal_notes-6ac741039e501ea4) running 227 tests test anchor::tests::lines_by_utf16_index ... ok test backlinks::tests::snippets_are_the_trimmed_line ... ok test anchor::tests::hash_is_plain_sha256_hex ... ok test backlinks::tests::links_are_fold_keys_and_lines ... ok test blocks::tests::finds_list_children_and_heading_sections ... ok test blocks::tests::line_edits ... ok test blocks::tests::shapes_keep_markers ... ok test blocks::tests::titles_from_the_first_sentence ... ok test bulk::undo::tests::inverse_restores_the_text ... ok test diff::tests::an_inserted_line_starts_at_a_line_start ... ok test diff::tests::emoji_positions_are_utf16 ... ok test diff::tests::small_change_is_a_small_edit ... ok test frontmatter::tests::h1_skips_code_and_frontmatter ... ok test frontmatter::tests::quoting ... ok test frontmatter::tests::reads_keys ... ok test frontmatter::tests::set_adds_a_line_or_a_block ... ok test frontmatter::tests::set_changes_only_the_value ... ok test frontmatter::tests::set_is_utf16_aware ... ok test history::tests::appends_are_ordered_and_readable ... ok test history::tests::pending_save_promotes_to_base ... ok test index::tests::a_shared_path_answers_with_the_newest_note ... ok test index::tests::a_torn_tail_cannot_swallow_the_next_durable_mapping ... ok test bulk::undo::tests::inverse_roundtrips ... ok test diff::tests::edits_turn_old_into_new ... ok test index::tests::compacts_when_it_grows ... ok test index::tests::reload_speed ... ignored test index::tests::survives_reload_renames_and_torn_lines ... ok test diff::tests::edits_never_split_a_character ... ok test index::tests::under_finds_exact_paths_and_descendants_with_folded_names ... ok test index::tests::under_keeps_shadowed_paths_for_watcher_removal ... ok test links::tests::finds_standard_and_old_style_links ... ok test index::tests::unloads_and_reloads ... ok test links::tests::nested_brackets_in_text ... ok test links::tests::relative_paths ... ok test links::tests::resolves_relative_to_the_linking_note ... ok test links::tests::retarget_follows_the_title_and_keeps_custom_text ... ok test links::tests::retarget_from_a_subfolder ... ok test links::tests::skips_code_images_and_the_web ... ok test links::tests::retarget_matches_case_and_unicode_forms ... ok test meta::tests::cursors_round_trip_and_reject_junk ... ok test meta::tests::snippets_skip_frontmatter_and_headings ... ok test missing::targets::tests::a_name_is_the_file_without_its_folder_and_extension ... ok test missing::targets::tests::names_rank_exact_then_prefix_then_word_then_inside ... ok test rename::tests::clash_numbers ... ok test rename::tests::daily_names ... ok test rename::tests::h1_is_set_or_added ... ok test room::acks::tests::varuints_round_trip ... ok test room::edit::tests::checks_bounds_overlap_and_surrogates ... ok test room::save::tests::concurrent_typists_and_saves_converge ... ok test room::save::tests::review::review_acknowledged_save_child ... ok test room::save::tests::kill_nine_mid_save_recovers_title_and_bytes ... ok REVIEW574 pending delete before insert, reload, deleted heading and live trailing format markers agree test room::save::tests::review::review_delete_set_before_insert_and_trailing_format_marks ... ok REVIEW574 failure retry: WriteFails, ErrorAfterWrite, save-time external merge and following title-only save agree test room::save::tests::review::review_failed_save_retry_and_outside_retry_keep_base_cache ... ok REVIEW574 SIGKILL after Notes::save reply: exact file hash, text, version, recovered cache and next title-only save agree test room::save::tests::review::review_kill_nine_after_save_reply_preserves_exact_file_history_and_version ... ok test index::tests::directory_counts_match_full_scan_after_moves_and_reload ... ok test room::save::tests::save_kill_child ... ok test index::tests::answers_like_plain_maps ... ok test room::save::tests::title_sync_does_not_decode_the_saved_document ... ok test scan::tests::day_ids_name_their_files ... ok test scan::tests::state_round_trips_and_skips_odd_names ... ok test search::tests::a_note_becomes_a_document ... ok test search::tests::plain_search_lines_borrow_their_text ... ok test search::tests::search_lines_keep_link_text_and_replacement_order ... ok test search::tests::titles_fall_back_to_the_file_name ... ok test smartline::tests::a_plain_line ... ok test smartline::tests::only_tags_are_words ... ok test smartline::tests::trailing_tags_only ... ok test stamp::tests::formats_with_offset ... ok test tags::front::tests::every_way_of_writing_tags ... ok test tags::merge::tests::property_tags_go_with_their_comma_or_line ... ok test tags::merge::tests::text_tags_go_with_one_space ... ok test tags::rewrite::tests::merging_keeps_every_tag_once_at_most ... ok test tags::rewrite::tests::nothing_to_do_is_no_edits ... ok test tags::rewrite::tests::only_real_tags_change ... ok test tags::rewrite::tests::positions_count_utf16 ... ok test tags::rewrite::tests::properties_change_name_only ... ok test tags::rewrite::tests::rename_and_back_is_the_same_text ... ok test tags::rewrite::tests::renames_tags_and_nested_tags_only ... ok test tags::rewrite::tests::renaming_onto_an_existing_tag_merges ... ok test tags::rewrite::tests::words_code_links_and_headings ... ok test tags::scan::tests::code_is_never_tags ... ok test tags::scan::tests::hash_free_lines_still_open_and_close_code_fences ... ok test tags::scan::tests::headings_and_quotes_keep_their_tags ... ok test tags::scan::tests::link_targets_and_addresses_are_never_tags ... ok test tags::scan::tests::not_tags ... ok test tags::scan::tests::plain_tags ... ok test tags::tests::nesting_and_cleaning ... ok test tags::tests::properties_and_text ... ok test tests::acks::an_older_lineage_is_refused_before_anything_is_sent ... ok test tests::acks::checks_are_answered_in_order ... ok test tests::acks::lineage_survives_a_restart ... ok test tests::acks::saved_means_the_change_is_in_history ... ok test tests::anchors::an_anchor_follows_its_block_when_lines_shift ... ok test tests::anchors::an_edited_block_is_refused_never_the_wrong_one ... ok test tests::anchors::anchors_need_a_hash_and_block_ids_work ... ok test tests::anchors::history_folds_typing_and_names_jobs ... ok REVIEW574 completed seed 1 test tests::anchors::shifting_lines_never_move_the_wrong_block ... ok test tests::appends::an_append_to_a_long_note_stays_small ... ok REVIEW574 completed seed 2 test tests::appends::appends_built_directly_match_the_copy_byte_for_byte ... ok REVIEW574 completed seed 3 test tests::appends::typists_and_appends_interleave_on_one_note ... ok test tests::appends_review::delete_whole_tail_then_append_matches ... ok test tests::appends_review::frontmatter_map_write_falls_back ... ok test room::save::tests::saved_bytes_and_titles_match_the_old_copy_path ... ok A2 old=[1, 1, 233, 7, 0, 196, 232, 7, 2, 9, 11, 3, 65, 50, 10, 0] A2 new=[1, 1, 233, 7, 0, 196, 232, 7, 2, 9, 11, 3, 65, 50, 10, 0] byte-identical=true test tests::appends_review::trailing_format_marker_bytes ... ok test tests::fanout::a_crash_before_the_seed_is_durable_never_doubles_text ... ok REVIEW574 completed seed 4 test tests::fanout::a_crash_between_fan_out_and_disk_loses_nothing ... ok test tests::fanout::a_first_open_does_not_wait_for_the_disk ... ok REVIEW574 completed seed 5 test tests::fanout::a_version_always_matches_its_text ... ok test tests::fanout::bursts_converge_and_reach_history ... ok REVIEW574 title matrix: 196 base/live pairs, including absent/empty/conflicting/duplicate/malformed titles, hidden H1s, whitespace and Unicode test room::save::tests::review::review_title_precedence_edge_matrix ... ok test tests::fresh::a_note_made_with_text_opens_with_its_frontmatter ... ok test tests::kill9::child ... ok test tests::kill9::delete_killed_after_its_answer_finishes_once ... ok test tests::kill9::permanent_delete_after_restore_cannot_delete_the_live_note ... ok test tests::kill9::quick_delete_restore_survives_kill ... ok test tests::kill9::quick_restore_delete_survives_kill ... ok test tests::kill9::restore_delete_forget_does_not_resurrect_after_kill ... ok test tests::kill9::restore_killed_after_its_answer_finishes_once ... ok test tests::fanout::collaborators_see_typing_before_the_disk ... ok test tests::listing::filters_by_tag_kind_and_created_range ... ok test tests::listing::pages_cover_every_note_once_in_a_stable_order ... ok REVIEW574 completed seed 6 test tests::listing::pinning_shows_in_the_list_and_unpinning_leaves_no_trace ... ok test tests::listing::the_first_page_never_waits_for_the_home_to_open ... ok test tests::listing::a_common_tag_pages_the_same_in_every_order ... ok REVIEW574 completed seed 7 test tests::listing::the_list_stays_live_with_new_notes_saves_and_outside_edits ... ok fast appends: 4003 test tests::appends_review::fuzz_deletes_restarts_parked ... ok test tests::missing::a_path_tells_here_trash_and_gone ... ok test tests::missing::a_rename_in_the_app_leaves_the_old_path_moved ... ok test tests::missing::make_from_link_makes_the_note_where_the_link_points_and_undo_takes_it_back ... ok test tests::missing::never_made_targets_are_offered_with_their_capitals_and_link_counts ... ok test tests::missing::never_written_paths_are_new_and_bad_ones_too ... ok test tests::missing::notes_in_the_trash_from_before_are_found ... ok test tests::missing::restored_with_a_new_name_reads_as_moved ... ok test tests::missing::a_move_outside_is_found_and_relink_repoints_the_links ... ok test tests::opening::a_cold_fill_ends_with_every_index_and_right_answers ... ok test tests::opening::a_note_never_typed_in_is_still_forgotten_on_restart ... ok test tests::opening::a_warm_open_announces_only_what_changed_while_closed ... ok test tests::opening::a_cold_home_indexes_every_file_once_with_its_own_id ... ok test tests::opening::identifying_an_already_indexed_file_never_makes_it_a_copy ... ok test tests::opening::shutting_down_leaves_the_list_log_empty ... ok test tests::opening::typing_saved_only_in_history_survives_a_restart_before_the_first_save ... ok test tests::listing::the_kept_count_follows_adds_and_deletes ... ok test tests::opening::upgrading_without_a_saved_scan_announces_nothing_for_known_notes ... ok REVIEW574 completed seed 8 test tests::outside::edit_while_closed_is_merged_on_open ... ok test tests::outside::missed_watcher_event_is_caught_before_saving ... ok test tests::outside::copied_note_gets_a_fresh_id ... ok test tests::opening::asking_mid_scan_never_says_not_found_and_never_duplicates_a_day ... ok test tests::outside::outside_create_and_delete ... ok test tests::outside::outside_edit_while_typing_merges ... ok REVIEW574 completed seed 9 test tests::outside::outside_rename_keeps_the_id ... ok test tests::peek::an_open_room_answers_with_its_live_text ... ok test tests::peek::reads_files_without_opening_rooms ... ok test tests::peek::returns_at_most_fifty ... ok test tests::pending::apply_places_changes_in_any_order ... ok test tests::pending::changes_out_of_order_all_arrive_and_saved_is_true ... ok test tests::pending::yrs_alone_loses_a_parked_change ... ok REVIEW574 completed seed 10 test tests::outside::own_saves_are_not_outside_edits ... ok test tests::renames::a_taken_name_gets_a_number_and_daily_notes_keep_theirs ... ok test tests::outside::polling_finds_outside_changes ... ok test tests::renames::backlinks_list_linked_and_unlinked_mentions ... ok test tests::renames::make_note_moves_the_block_and_undoes_as_one_step ... ok test tests::renames::move_block_to_another_note_and_back ... ok test tests::renames::rename_moves_the_file_and_repoints_every_link ... ok REVIEW574 completed seed 11 test tests::renames::typing_a_new_title_renames_the_file_after_a_pause ... ok test tests::renames::backlinks_follow_edits_and_deletes ... ok test tests::renames::undo_keeps_a_made_note_someone_typed_in ... ok test tests::renames::typing_in_a_linking_note_during_a_rename_is_kept ... ok test tests::rooms::late_joiner_gets_full_state ... ok test tests::rooms::patches_use_utf16_positions ... ok test tests::rooms::fresh_note_opened_by_many_at_once_is_not_doubled ... ok test tests::rooms::retried_change_applies_once ... ok test tests::rooms::retried_create_makes_one_note ... ok test tests::rooms::stale_replace_keeps_newer_typing ... ok test tests::rooms::typing_survives_a_concurrent_delete ... ok test tests::saving::a_write_error_keeps_the_change ... ok REVIEW574 completed seed 12 test tests::rooms::random_concurrent_editing_converges_without_duplicates ... ok test tests::saving::an_error_after_the_write_does_not_double_text ... ok test tests::saving::crash_after_write_does_not_double_text ... ok test tests::saving::failed_save_keeps_the_change ... ok REVIEW574 completed seed 13 test tests::saving::first_keystroke_creates_the_file ... ok test tests::saving::line_endings_and_bom_are_kept ... ok test tests::saving::crash_before_write_loses_nothing ... ok REVIEW574 completed seed 14 test tests::saving::opening_never_writes ... ok REVIEW574 completed seed 15 test tests::saving::saving_does_not_loop ... ok test tests::saving::title_and_h1_stay_in_step ... ok REVIEW574 completed seed 16 test tests::saving::nonstop_typing_still_saves ... ok test tests::renames::a_crash_mid_rename_resumes_and_applies_each_edit_once ... ok REVIEW574 completed seed 17 test tests::storm::outside_rewrites_during_a_storm_all_land ... ok test tests::tags::bad_names_are_refused ... ok test tests::tags::renames_every_tag_and_undoes_as_one_step ... ok test tests::tags::typing_during_a_tag_rename_is_kept ... ok test tests::trash::a_closed_note_is_trashed_without_opening_it ... ok test tests::tags::a_big_tag_rename_never_stalls_typing ... ok test tests::trash::a_file_deleted_outside_goes_to_the_trash ... ok test tests::trash::a_never_typed_note_is_simply_forgotten ... ok test tests::trash::a_note_deleted_before_its_first_save_keeps_its_text ... ok test tests::trash::delete_and_restore_answer_once_per_job_id ... ok test tests::trash::delete_moves_to_trash_and_undo_brings_it_back ... ok test tests::trash::delete_replay_with_a_stale_index_keeps_the_only_file ... ok test tests::trash::folder_rename_and_outside_move_bursts_keep_every_descendant ... ok test tests::trash::forget_deletes_file_and_history_forever ... ok test tests::trash::half_written_trash_folders_never_hide_a_live_note ... ok test tests::trash::restore_durably_indexes_a_note_without_an_embedded_id ... ok test tests::trash::restore_keeps_id_and_history_and_takes_a_free_name ... ok test tests::trash::restore_with_a_stale_index_really_moves_the_file_back ... ok test tests::trash::the_sweep_empties_only_notes_older_than_thirty_days ... ok test tests::trash::trash_ids_never_reach_another_home ... ok REVIEW574 completed seed 18 test tests::trash::trash_pages_newest_first ... ok test tests::trash::trashed_notes_leave_search_lists_and_backlinks_and_come_back ... ok REVIEW574 completed seed 19 test tests::unloading::a_connected_client_keeps_it ... ok REVIEW574 completed seed 20 test tests::unloading::an_open_note_keeps_it ... ok test tests::unloading::an_unused_home_unloads_and_answers_after ... ok REVIEW574 completed seed 21 test tests::writers::every_writer_at_once_loses_and_doubles_nothing ... ok test tests::writers::every_writer_at_once_with_the_watcher ... ok test text::tests::mixed_endings_take_the_majority ... ok test text::tests::styles_round_trip ... ok test text::tests::uniform_files_round_trip_exactly ... ok test trash::tests::a_note_without_details_still_reads ... ok test trash::tests::details_without_a_note_are_not_a_note ... ok test trash::tests::missing_details_never_age_trash_from_the_notes_old_mtime ... ok test versions::tests::folds_runs_by_author_kind_and_gap ... ok test ydoc::tests::fork_and_merge_keeps_concurrent_typing ... ok test ydoc::tests::reapplying_an_update_changes_nothing ... ok test ydoc::tests::utf16_offsets ... ok REVIEW574 completed seed 22 REVIEW574 completed seed 23 test tests::trash::a_crash_mid_delete_finishes_on_restart_exactly_once ... ok REVIEW574 completed seed 24 REVIEW574 fuzz operation counts: [177, 198, 178, 166, 183, 173, 164, 181, 154, 170, 166, 180, 162, 148] REVIEW574 differential: 2424 saves, 264 fresh Notes/Home restarts; file bytes, CRDT state, title-update bytes, history replay and versions agree test room::save::tests::review::review_fuzz_old_new_bytes_history_versions_and_restarts ... ok test tests::tags::a_crash_mid_rename_resumes_and_renames_each_tag_once ... ok test result: ok. 226 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 13.39s GATE EXIT: 0 GATE: cargo clippy -j4 -p calternal-notes --all-targets -- -D warnings Checking cfg-if v1.0.5 Checking rand_core v0.10.1 Checking libc v0.2.190 Checking once_cell v1.21.4 Checking bitflags v2.13.2 Checking pin-project-lite v0.2.17 Checking serde_core v1.0.229 Checking smallvec v1.16.3 Checking linux-raw-sys v0.12.1 Checking typenum v1.20.1 Checking rustix v1.1.5 Checking getrandom v0.4.3 Checking hybrid-array v0.4.15 Checking zmij v1.0.23 Checking parking v2.2.1 Checking itoa v1.0.18 Checking cpufeatures v0.3.1 Checking memchr v2.8.3 Checking fastrand v2.5.0 Checking event-listener v5.4.2 Checking tracing-core v0.1.36 Checking foldhash v0.2.0 Checking scopeguard v1.2.0 Checking hashbrown v0.17.1 Checking lock_api v0.4.14 Checking serde v1.0.229 Checking serde_json v1.0.154 Checking tracing v0.1.44 Checking uuid v1.28.0 Checking thiserror v2.0.21 Checking ref-cast v1.0.27 Checking event-listener-strategy v0.5.4 Checking inotify-sys v0.1.8 Checking parking_lot_core v0.9.12 Checking crossbeam-utils v0.8.23 Checking crypto-common v0.2.2 Checking block-buffer v0.12.1 Checking hashbrown v0.14.5 Checking tinyvec v1.13.3 Checking dyn-clone v1.0.20 Compiling time-core v0.1.9 Checking log v0.4.34 Checking same-file v1.0.6 Checking const-oid v0.10.2 Compiling num-conv v0.2.2 Checking walkdir v2.5.0 Checking dashmap v6.2.1 Checking digest v0.11.3 Compiling time-macros v0.2.32 Checking mio v1.2.4 Checking unicode-normalization v0.1.25 Checking schemars v1.2.3 Checking tokio v1.53.2 Checking libsqlite3-sys v0.38.2 Checking inotify v0.11.5 Checking async-lock v3.4.2 Checking arc-swap v1.9.2 Checking hashlink v0.12.2 Checking tempfile v3.27.0 Checking wait-timeout v0.2.1 Checking smallstr v0.3.1 Checking notify-types v2.1.0 Checking bit-vec v0.10.1 Checking quick-error v1.2.3 Checking arrayvec v0.7.8 Checking fnv v1.0.7 Checking fallible-streaming-iterator v0.1.9 Checking fallible-iterator v0.3.0 Checking powerfmt v0.2.1 Checking deranged v0.5.9 Checking constant_time_eq v0.4.2 Checking blake3 v1.8.7 Checking calternal-core v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/core) Checking rusqlite v0.40.2 Checking rusty-fork v0.3.1 Checking bit-set v0.11.1 Checking notify v8.2.0 Checking yrs v0.28.0 Checking num-traits v0.2.19 Checking calternal-fsio v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/fsio) Checking sha2 v0.11.1 Checking chacha20 v0.10.2 Checking rand v0.10.3 Checking calternal-memory v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/memory) Checking rand_xorshift v0.5.0 Checking regex-syntax v0.8.11 Checking core_detect v1.0.0 Checking base64 v0.22.1 Checking time v0.3.55 Checking hashbrown v0.16.1 Checking unicode-segmentation v1.13.3 Checking similar v3.2.0 Checking unarray v0.1.4 Checking calternal-testkit v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/testkit) Checking proptest v1.12.0 Checking calternal-notes v0.1.0 (/home/kayg/Developer/calternal-wt-574r/crates/notes) Finished `dev` profile [unoptimized + debuginfo] target(s) in 19.16s GATE EXIT: 0 ```
Author
Owner

Landed in train AB, #580.

Train AB ran the full suite: fmt, clippy, workspace tests (502 passed, 0 failed), the build, svelte-check, and every Playwright project in parallel shards.

  • First run discarded: the Rust steps hadn't compiled (a broken shared environment file), so e2e had used an old binary. The train script now stops at the first failing Rust step.
  • Rerun: every shard passed except two tests that failed under load:
    • swipe-rows:116 passed 60 of 60 repeated alone;
    • missing-links:155 (page closed) passed 54 of 54.
  • webkit-390 shard 3/3: it collided on a port, was rerun alone, and passed 94 of 94.

Contents:

  • #576: the block-selection tint covers exactly its block;
  • #578: the phone layout never scrolls sideways;
  • #575: swipe rows;
  • #573: narrow handle and link pill padding;
  • #574: saves no longer copy the whole note, with the independent review's crash tests.
Landed in train AB, #580. Train AB ran the full suite: fmt, clippy, workspace tests (502 passed, 0 failed), the build, svelte-check, and every Playwright project in parallel shards. - **First run discarded:** the Rust steps hadn't compiled (a broken shared environment file), so e2e had used an old binary. The train script now stops at the first failing Rust step. - **Rerun:** every shard passed except two tests that failed under load: - `swipe-rows:116` passed 60 of 60 repeated alone; - `missing-links:155` (page closed) passed 54 of 54. - **webkit-390 shard 3/3:** it collided on a port, was rerun alone, and passed 94 of 94. Contents: - #576: the block-selection tint covers exactly its block; - #578: the phone layout never scrolls sideways; - #575: swipe rows; - #573: narrow handle and link pill padding; - #574: saves no longer copy the whole note, with the independent review's crash tests.
kayg closed this pull request 2026-10-18 12:07:50 +00:00
Some checks failed
CI / web (pull_request) Failing after 9s
CI / server (pull_request) Successful in 24s
CI / rust (pull_request) Successful in 2m33s

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal!574
No description provided.