Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
8.1 KiB
Test evidence: delivery
What each delivery test guards and which injected fault it was seen to fail on. Keyed by test name.
The reasoning is in context/code/delivery.md. Each fault was put into the source one at a time,
the suite run, the fault reverted with git checkout -- <file>, and the suite run again.
The host-side tests are in crates/improvise-command/src/ui/delivery_tests.rs, the client's in
crates/improvise-protocol/src/delivery/client_tests.rs, the wire's in delivery/wire.rs.
The injected faults, by what failed
- The host reports no moved keys (
let keys = BTreeSet::new()inDelivery::commit): seven host-side tests fail:a_dropped_connection_or_a_failed_request_triggers_a_correction_and_the_session_resumes,a_pull_only_client_follows_the_log_without_a_correction,a_notice_names_the_moved_cells_including_a_formula_cell_it_did_not_write,values_keep_every_bit_from_the_cell_to_the_client,an_oversized_notice_says_correct_and_costs_every_reader_a_correction,a_dropped_notice_is_detected_and_a_correction_repairs_itand the random property.a_frame_of_two_commands_publishes_one_notice_each_and_no_client_asks_for_a_correctionstill passes, on purpose: itsAddItemmakes the client refetch the window, which also fetches the write. - The client treats every notice as contiguous (
false => trueinon_notice):a_dropped_notice_is_detected,a_dropped_notice_is_detected_and_a_correction_repairs_itand the random property. - A failed frame does not put the
Deliveryback (undo_framedropspublished):a_refused_command_consumes_no_publication_number,commands_before_a_refused_one_are_rolled_back_with_their_notices_under_adr_001anda_refused_frame_is_never_read_back. - A moved correction replaces instead of subtracting held (
targetfortarget - held_there):a_moved_correction_applies_only_at_the_watermark_it_starts_fromanda_moved_key_the_target_no_longer_holds_is_removed. The same for a window correction (targetfortarget - held):a_correction_equals_the_target_and_a_second_one_is_the_zero_zset,a_correction_replaces_what_is_held_not_adds_to_itanda_correction_already_covered_is_idempotent_and_never_regresses. - A wire number loses the sign of zero (
Num::inexact(x + 0.0)inWireNum::of_f64):a_float_crosses_in_canonical_text_with_the_sign_of_zeroanda_float_keeps_every_bit_but_the_nan_payload. coveredignores the log's start and the shape's liveness:a_watermark_the_log_covers_gets_the_moved_keys_and_one_it_does_not_gets_the_windowandan_evicted_shape_is_announced_and_its_reader_corrects.- The client does not remember an axis change (
restructurednever set):an_axis_change_that_shifts_an_unmoved_cell_into_the_window_refetches_the_window. The host's ownrestructuredtest alone fails only the random property, since the client'swindowflag also forces a window. Stamp::same_epochignores the host instance:a_notice_from_another_host_or_generation_asks_for_a_correction,a_stamp_from_another_epoch_is_behind_every_current_one,a_reconnect_after_a_host_restart_asks_for_a_correction_from_the_old_watermarkanda_notice_from_a_restarted_host_asks_for_a_correction_too.
Entries
a_refused_frame_is_never_read_back
The first fixture filled the document to the category limit and then refused the frame at its basis
check, so the write never ran and the test passed with the Delivery not restored. A fixture that fails
in a wider shape than the fault: the frame never reached mutating_frame. It now writes North, then
loads a missing document, and asserts Rejection::MutationRefused so the write is known to have run. The
rival above fails it with 77.0 held.
a_corrected_state_is_the_target_and_correcting_it_again_is_the_zero_zset
The random property: histories of up to twelve single-command frames (writes of 0.0, -0.0, both
infinities, NaN and 0.1 + 0.2, item adds and removes, a formula), a random window, notices delivered
or dropped, connections lost, requests dropped, replies dropped and garbled. It ends with a poll, then
asserts the client holds exactly what the grid draws in its window, and that target - held is the zero
Z-set. The oracle (drawn) reads GridLayout itself and shares nothing with Output.
Without the closing poll a lost last notice is not detectable by a listener, and the property
reported it as a stale client (minimal case: one Remove, notice dropped, no later notice).
The first version of the property also failed on a window of one cell after a remove, and that was a
real gap in the correction, not in the test: the remove moved South into the window without moving its
value. It is the reason for the restructured rule (delivery.md §3).
a_frame_of_two_commands_publishes_one_notice_each_and_no_client_asks_for_a_correction
Step 8's first required test. The frame's first command carries one basis stating both records
(Host's frame rule: the first mutation's basis stands for the frame), which the test helper builds.
commands_before_a_refused_one_are_rolled_back_with_their_notices_under_adr_001
Step 8 requires that the commands before a refused one stay published. Today's host undoes the whole frame (ADR 001 clause 4), so this test asserts the opposite and names the reason. Flip it when ADR 006 makes each command commit on its own.
a_dropped_connection_or_a_failed_request_triggers_a_correction_and_the_session_resumes
The three transport faults (request dropped, reply dropped, reply unreadable) each leave the client
asking for Ask::Correction from the watermark it held, and the next request carries the same session
token and that watermark.
an_axis_change_that_shifts_an_unmoved_cell_into_the_window_refetches_the_window
A one-cell window, a remove that moves South into it. The notice lists only North, so a client that fetches moved keys alone ends with an empty window.
Review of #348, round one
Each test below was written against the behaviour the review reported and failed before its fix, or, for the cost tests, was run against a quadratic rival.
diffing_a_category_compares_each_item_a_bounded_number_of_times
Items are a type whose eq is counted. Rival: contains on the slices in diff_items, which makes the
count quadratic and fails every size. A wall-clock test would have passed on a fast machine.
a_commit_with_nothing_live_and_no_transport_reads_nothing
Counts Structure::of calls. Rival: the idle skip removed.
the_outbox_and_the_clone_a_frame_takes_are_bounded
Rival: the outbox truncation removed, so twenty commits leave twenty queued.
a_late_window_reply_from_an_older_generation_does_not_regress
Rival: the older-generation guard disabled (fails this test in the protocol crate).
a_late_notice_from_a_dead_host_settles_against_the_live_one
Rival: the duplicate arm no longer prunes other epochs' entries; the random property also fails.
a_poll_for_one_shape_leaves_the_other_shapes_alone
Rival: pulled notices applied to every shape.
a_transpose_empties_a_partial_window_of_the_cell_that_left_it
A transpose moves no key. Rival: flags.views not counted as a structural change in the client and the
host. a_dropped_views_notice_is_answered_with_the_window_not_an_empty_key_list fails with the host
alone changed.
wide_integers_are_canonical_on_input
Both spellings are refused outside their range: a JSON number at or above 2^53, a string below it, a sign, leading zeros and trailing space.
an_item_insert_position_crosses_as_a_wide_integer
ItemInserted.at is a u128 through wide.
a_fetch_covers_only_the_notices_it_named_when_a_later_commit_is_already_in_the_state
Rival: a fetch answered with through: at. The first version of the random property did not catch it (it
delivered each notice before the next commit); the property now splits, delays, duplicates and replays
notices and old replies, and fails the rival on every run, which this test also does by name.
equal_values_compare_equal_whatever_the_zero_counts_say
ExactSum's equality compared the sign-of-zero counters even when the total was nonzero.