Merge gh/main (202e7eac) into collapse-fu/wm20

# Conflicts:
#	context/test-evidence/command.md
This commit is contained in:
Edward Langley
2026-09-23 17:19:55 -07:00
47 changed files with 2238 additions and 298 deletions
+133 -2
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
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 wire `Command::Model` cannot reach the document without its basis being checked
@@ -5657,6 +5679,46 @@ across two independently built ones. If a future refactor made the fixture
share one map, or share one `RandomState`, the second assertion fails. Without
this test the law would then pass everything and look healthy.
### changing_the_target_renames_the_formula_in_its_own_place
improvise-km71. This is the test that separates "edit" from "add". The rival
was the first build: `commit-buffer formula` was resolved in `parse_commit_kind`,
where the registry's parse-first rule never reaches the mode, so every edit
became an `AddFormula`. `committing_an_edit_replaces_the_formula_in_place`
**passed** under that rival, because `add_formula` upserts by
`(target, target_category)` and an edit that keeps its target lands in the same
slot. Injected on purpose (the commit emits `AddFormula` for the replace kind):
this test, the rename-status test, the collision test, the parse-error test and
both concurrency-refusal tests fail, and the in-place test passes. A rename is the input the upsert cannot fake. The fixture has three
formulas with the cursor on index 1, so "the one under the cursor" is not also
"the first" or "the last".
### an_edit_lands_on_its_formula_after_an_earlier_one_is_deleted_meanwhile
improvise-km71. The capture is made when the editor opens and never re-read
from the cursor. Rival: `ReplaceFormula` resolves its formula from
`formula_cursor` at commit. The fixture deletes `Profit` (index 0) while the
editor is open on `Tax` (index 1), so cursor 1 now names `Margin`. The rival
rewrote `Margin`, and this test fails, along with
`an_edit_whose_formula_was_deleted_meanwhile_refuses_out_loud` and
`an_edit_whose_formula_was_changed_meanwhile_refuses_out_loud`. The other nine
formula-edit tests pass under it, because none of them moves the list.
### an_edit_whose_formula_was_changed_meanwhile_refuses_out_loud
improvise-km71. Another client rewrote `Tax`'s text but kept its target, so the
id still resolves. Only the `was` check can see this. Rival: the `was` filter
deleted. This test is the only one that fails. That is the case for keeping
`was` after improvise-sboz gives formulas a stable id: a concurrent replace
keeps the id.
### a_rename_onto_another_formulas_target_refuses_and_keeps_both
improvise-km71. Rival: `Model::replace_formula`'s collision check disabled.
This test and `replace_formula_refuses_to_displace_another_formula` fail.
Without the check, renaming `Tax` to `Margin` leaves two formulas with one
identity, and `formula(&id)` then finds the first.
### a_fresh_session_has_no_history_to_step_back_through
improvise-lxg0. `project.rs::fresh_view` built a new session's slice by folding
@@ -5802,8 +5864,10 @@ client sends `SetCell` (then, separately, `ClearCell`) for `{Month=Q2, _Measure=
with a caught-up basis. At `ea119bd9` both were answered `Accepted`, and the
`SetCell` stored `Month=Q2, _Measure=Cost = 7`, the non-item coordinate
improvise-gw18 exists to prevent. The test asserts `Rejected(MutationRefused)`
and an unchanged document for both. Rival: the aggregate refusal disabled. This
test and the None-axis test fail. The control is
and an unchanged document for both. Rival: the aggregate refusal disabled. At
`46e75c41` that failed this test and the None-axis test. Since improvise-b114 a
fold is refused first by the group rule, so this rival fails only the None-axis
test. Disabling both rules fails this test. The control is
`a_wire_write_to_a_stored_cell_beside_a_fold_is_accepted`, so a host that refused
every wire write cannot pass.
@@ -5846,6 +5910,39 @@ that the target projects as `Record`, so a fixture where the first column is
harmless cannot pass. The fix looks the cell up by its whole key. The rival is
the old lookup by record, which is this test's red commit, `6e021699`.
### a_wire_write_naming_a_measure_group_is_rejected_in_any_view
improvise-b114, found in the review of PR #217 at `bc060f67` (the reviewer's r3).
`_Measure` is on Column, Revenue and Cost are grouped as `Money`, and `Units` is
ungrouped. Every month is funded. A caught-up `SetCell` on
`{Month=Feb, _Measure=Money}` was answered `Accepted` and stored
`Month=Feb, _Measure=Money = 7`, a non-item coordinate. Its record `{Month=Feb}`
is stored, so the stored arm accepted it without asking the projection. The test
runs with `Money` collapsed, where the host draws that cell as the fold, and
expanded, where the host draws no such cell. It asserts `MutationRefused` and an
unchanged document in both, so a rule that asks the host's layout cannot pass
the expanded half. Red at `5eaafbae`. The fix is `App::group_refusal`, which asks
the document whether any coordinate names a group rather than an item. Rival:
the group rule removed. Injected into a full workspace run, it fails this test
and nothing else.
### a_wire_write_to_a_fold_record_with_an_undrawn_measure_is_rejected
improvise-xur5, found in the delta re-review of PR #217 at `6026768c`. It is the
reviewer's `review_217b::probe_fold_hidden_measure`, widened to all three rows
of the reviewer's table. Q2 is collapsed and every month is stored, so the
record `{Month=Q2}` is unstored and resolves only through the layout, at the
fold row. A caught-up write names that record with a measure the host draws
nowhere: `Cost` hidden in the host's view, `Bogus`, which is no measure at all,
or no measure. At `6026768c` all three were **accepted** and stored `Month=Q2`,
a group name, as a coordinate. The sjdb fix finds the position by the whole key,
and "not drawn" read as "not refused". Falling back to the by-record lookup for
an undrawn key would close this, but the reviewer showed it wrongly refuses
Feb × Revenue beside a collapsed `Money` group, so it is not the fix. The
improvise-b114 rule is: no coordinate names a group, asked of the document.
Rival: the group rule removed. Injected into a full workspace run, it fails this
test and the b114 test, and nothing else.
### a_stored_record_the_hosts_view_aggregates_is_writable
improvise-sjdb, from the #217 review. South/Revenue is stored, then `Product` is
@@ -5932,6 +6029,16 @@ host's `Normal`, and nothing else different. `install_workbook` needs no
change of its own, because both of its callers (`LoadModel`,
`AcceptWizardModel`) run inside `apply_effects`, which settles after the batch.
### renaming_a_formula_outside_measure_refuses_and_keeps_the_editor
improvise-kb1d, found by the pre-merge review of #214 and by ChatGPT Codex (P1).
The fixture has a Region-targeted formula at index 1, between two `_Measure`
formulas, because every other km71 test used `_Measure`, whose items are
derived from the formula list and cannot show a stranded item. Rival: the guard
in `Model::replace_formula` disabled. This test fails on the formula list, and
`replace_formula_refuses_to_rename_a_target_outside_measure` in core fails too.
### `an_untouched_pivot_edit_of_a_misread_value_writes_nothing`
### `an_untouched_records_edit_of_a_misread_value_writes_nothing`
@@ -6141,3 +6248,27 @@ improvise-b5dp, the second case the bead named. `Share = Revenue /
SUM(Revenue)`: East's own `SUM` is 0, so its row draws `div/0`. The pooled
total is 60/60 = **1.00**. It was green at `9948aaf5` and fails under the same
rival as the test above.
### a_frame_that_reframes_and_then_places_the_cursor_leaves_the_asker_placed
improvise-wdmp. A browser frame that reframes the grid and then places the cursor,
for example the records `+cat` commit `[ChangeMode, AddCategory, SetSelected]`, is
folded by the client all at once and before any answer arrives. The host's mirror
folds the same frame in order. `adopt_cursor_epoch` resets the mirror at the
mutation, and the later `SetSelected` places it. The relayed `ResetCursor`
reached the client **after** its own placement and undid it. So the client sat at
the origin while its mirror, and every projection built from it, sat at the
placement. That is a split, not merely the jump the ticket described. The test
drives a real `Host` and a real `ClientState` through `fold_outgoing`, then folds
every frame addressed to the asker. It seats the cursor off the origin first, so
a reset that never arrived is visible. It runs two shapes: with a placement
after the reframe (expect `(1, 0)`), and without one (expect `(0, 0)`, gvnb's
reset). In both, the client's cursor must equal its mirror's. At the base the
placed case failed: the client was at `(0, 0)` and the mirror at `(1, 0)`.
Two rivals. First, the echo disabled, which is the pre-fix behaviour: the placed
case fails. Second, the ticket's suggested fix, which drops the asker's
`ResetCursor` from the relay: the unplaced case fails, because the mirror was
reset and the client never heard. That rival also fails
`a_geometry_command_relays_the_cursor_reset_to_the_asker_too` and the
ws-server's `the_other_document_would_also_have_a_cursor_to_invalidate`.