improvise-lxg0/w1bu: evidence, and command.md describes the fixed state

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-22 22:42:34 -07:00
parent a9a5b3075a
commit b22f6e5389
2 changed files with 52 additions and 11 deletions
+14 -9
View File
@@ -818,15 +818,20 @@ would therefore drop a frame the client keeps, and the host and the client
would disagree. The first version of this fix made exactly that mistake.
`colon_view_back_respects_whether_history_exists` caught it, because its
fixture is such a frame. `reduce_view`'s `stepped` is unchanged, because the
client holds no `Workbook` to observe against. That leaves the host and the
client able to disagree wherever a client's stack holds a frame the document
would refuse. An earlier version of this paragraph said such a frame could not
exist, because `ForgetView` purges deleted views and a load arrives as a fresh
`Snapshot`. **That is false.** `project.rs::fresh_view` starts from
`ViewState::default()` and folds `SwitchView(active)`, so every connected or
rebased client gets a phantom `{Default, Normal}` back-frame, and `Default`
need not exist in the document. That is improvise-lxg0, and until it is fixed
the divergence is live rather than theoretical.
client holds no `Workbook` to observe against. It is safe only while no client
stack holds a frame the document would refuse, which rests on three paths:
`ForgetView` purges a deleted view from every mirror, a load arrives as a fresh
`Snapshot`, and that snapshot's history starts **empty**. The third was false
until improvise-lxg0. `project.rs::fresh_view` used to start from
`ViewState::default()` and fold `SwitchView(active)`, which recorded a visit to
the literal `Default`. So every connected or rebased client carried a phantom
`{Default, Normal}` back-frame, whether or not the document had such a view.
It now builds the slice pointed at the document's active view and folds
nothing, as `App::with_storage` does. The guard is still one level away from
the hazard. A new path that seeds or replaces a client's history without
emptying it reopens the divergence, and nothing but
`a_fresh_session_has_no_history_to_step_back_through` and its rebase twin
would notice.
### `ReturnToNormalUnlessMoved` — observation, not prediction (improvise-toc)
+38 -2
View File
@@ -1993,8 +1993,10 @@ property: it is still not a `SetCell` target, because `typed_cell_command`,
`record_cell_at_cursor` and `render.rs::editable` all match `Record` alone.
The test seats the cursor on the **second** row, so the first-row and
first-item cases cannot coincide. It then enumerates every cell of the records
grid and requires each to carry an id, which is the ticket's claim with no
case picked by hand. Before the fix it failed at `a drawn coordinate projected
grid and checks each one's kind against its column's `RecordCol`
(`names_its_column`), which is the ticket's claim with no case picked by hand.
The first version of that enumeration checked only that each id was `Some`,
which was weaker than the test it replaced. See improvise-w1bu below. Before the fix it failed at `a drawn coordinate projected
as no cell`. Rival injected afterwards: `RecordCol::Category(_) => return None`
restored. This test failed at the `Some(CellId::Coordinate)` assertion, and
`a_creating_column_is_named_rather_than_left_as_no_cell` (whose `None`-count
@@ -5313,3 +5315,37 @@ refusal is recorded and the mode is not `Quit`. `import-json` failed first,
before `refuse_and_stop` was applied to its arms. For `load`, the injected fault
is removing its abort (`refuse_and_stop` back to `app.refuse`), and the test
then fails with "`load absent.improv . q` quit having loaded nothing".
### a_fresh_session_has_no_history_to_step_back_through
improvise-lxg0. `project.rs::fresh_view` built a new session's slice by folding
`SwitchView(active)` into `ViewState::default()`. That default's `active_view`
is the literal `Default`, so the fold pushed a `{Default, Normal}` back-frame
for a visit that never happened. The test connects to two documents, and both
open on `Second`. One **has** a `Default` view: there `<` would take the client
to a view it never visited, which is improvise-lrrx's symptom seen from the
client. One **has not**: there the frame names a dead view, and
`reduce_view::stepped` would step onto it anyway. A fixture that opens on
`Default` is right by accident, because the phantom frame then names where the
client already is. Before the fix both cases failed at the back-stack
assertion with `left: [ViewFrame { view_name: "Default", mode: Normal }]`.
The rival is that unfixed fold. The second assertion folds `ViewBack` and
checks that the client stays on `Second`, so a fix that emptied the stack
while still recording a move elsewhere cannot pass.
### a_rebased_session_has_no_history_to_step_back_through
improvise-lxg0, through the other door. `Sessions::rebase` reseeds through
`fresh_view` too. The session connects to a document opening on `Default`, then
`install_workbook` swaps in each of the two documents above. Against the
unfixed code it failed the same way.
### The records-grid enumeration checks kinds, not presence (improvise-w1bu)
The review of #196 injected `RecordCol::Category(c) if row == 0 => Some(c.clone())`
into `cell_id`. That projects row 0's coordinate as an editable `Record` with
`measure: Some("Region")`, and the whole workspace passed, because the
enumeration asked only "is every cell something". It now asks "is every cell
the right thing", column by column. Re-injected here, exactly one test in the
workspace failed, this one, with `a records cell of the wrong kind: [(0, 0,
Some(Record(RecordCell { .., measure: Some("Region") })))]`.