Document the canonical Debug fingerprint and its evidence (improvise-djcn)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
This commit is contained in:
Edward Langley
2026-09-23 08:47:21 -07:00
parent 6096c03caa
commit 47bab86a50
2 changed files with 46 additions and 0 deletions
+24
View File
@@ -512,6 +512,30 @@ change. `recomputing_formulas_leaves_the_model_fingerprint_alone` pins this.
`view_only_effects_leave_shared_state_alone` now runs over a formula-bearing `view_only_effects_leave_shared_state_alone` now runs over a formula-bearing
fixture, for the same reason. fixture, for the same reason.
**That fix was canonical within one instance, not across two (improvise-djcn).**
It fixed `Model`'s own maps but not the maps inside its fields. `DataStore` has
`index`, `records` and the symbol table, and `View` has `page_selections`,
`hidden_items` and `collapsed_groups`. All still printed through a derived
`Debug`, in hash order. Each `HashMap` gets its own `RandomState`, so two equal
documents built separately printed differently in 32 of 32 runs.
- The effect-classification tests compare one instance with itself before and
after, so they were unaffected.
- `a_model_command_lands_where_its_keypress_lands` compares **two** `App`s. It
passed only because its fixture had no cells and no multi-entry view maps.
Now:
- `DataStore`, `SymbolTable` and `View` have hand-written `Debug` impls. Each
sorts wherever its storage is a hash map.
- `DataStore` resolves symbols to their strings. A `Symbol`'s number is interning
order, not content, so printing it would make two equal stores differ again.
- `SymbolTable` prints its interned strings, sorted.
- `cells` keeps its `IndexMap` order, because that order is part of the document
(gotcha 18).
- Every hand-written `Debug`, `Model`'s included, **destructures** `self`. A field
added later is then a compile error there, where a `debug_struct` that names
fields one by one would skip it silently. The pc3b version of `Model`'s impl
had exactly that hole.
`parse_number_cell` returns `CellValue::Error` naming the offending text rather `parse_number_cell` returns `CellValue::Error` naming the offending text rather
than a fabricated `0.0`. No token the current grammar admits actually fails than a fabricated `0.0`. No token the current grammar admits actually fails
`f64::from_str` (overflow saturates to ±inf), but if the two ever drift the `f64::from_str` (overflow saturates to ±inf), but if the two ever drift the
+22
View File
@@ -205,6 +205,28 @@ Row, swapping the axes and rotating Row→Column land on the same view, and an
equivalence test that cannot distinguish the wrong effect from the right one equivalence test that cannot distinguish the wrong effect from the right one
proves nothing (`doctrine(check_the_checker)`). proves nothing (`doctrine(check_the_checker)`).
**The fixture needs cells and multi-entry view maps too (improvise-djcn).** The
test compares `format!("{:?}", workbook)` across **two** `App`s. It passed only
because the fixture had no cells and every view map had at most one entry, and a
`HashMap` of one entry has only one order. The fixture now carries:
- three cells;
- two page selections;
- two categories with hidden items, one of them with two;
- two categories with collapsed groups.
That fixture was red in 3 of 3 runs on the old `Debug` impls: the by-key and
by-command fingerprints differed only in hash order. It was green after the fix.
Each half of the fix was checked by restoring the derived `Debug` on its own:
- `View`'s derived `Debug` alone: 3 of 3 runs red.
- `DataStore` and `SymbolTable`'s derived `Debug` alone: 3 of 3 runs red.
So neither half is carried by the other. The exhaustive destructure in `Model`'s
`Debug` has no test of its own, because it is a compile-time guard. It was
checked by adding a `probe_field` to `Model` and to `Model::new`. The build then
failed with `E0027: pattern does not mention field probe_field` at the `Debug`
impl, and nowhere else.
### a_stale_wire_model_command_cannot_be_applied ### a_stale_wire_model_command_cannot_be_applied
A wire `Command::Model` cannot reach the document without its basis being checked A wire `Command::Model` cannot reach the document without its basis being checked