WIP: evidence and context for improvise-ixxx

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:27:50 -07:00
parent 4c45166b8a
commit 9d3fe197df
3 changed files with 153 additions and 1 deletions
+7 -1
View File
@@ -1411,6 +1411,12 @@ than a check; or this client has not been told the document's basis and so has
nothing truthful to say about what it composed against (`BasisCache::claim`
withholds rather than invents).
A **fifth** `None` is not a failure: the text is the seed the editor captured
when it opened (improvise-ixxx). The page passes that seed as `typed_cell`'s
fourth argument, and `resolve::untouched`, the one home beside `typed()`,
decides. The argument is in `protocol.md` §3, "An untouched editor writes
nothing".
**Nothing is folded there.** The new value comes back as a `GridUpdate::Cells`
from the host, which is the side that formats it; guessing it locally is how the
two ends drift apart.
@@ -1565,7 +1571,7 @@ whether a typed string is a number. Five calls are the whole harness:
| `client.set_viewport(rows, cols)` | the handshake frame, and every resize; also records the window so keys resolve here |
| `client.on_frame(json)` | fold one frame, in arrival order |
| `client.render_model()` | everything to paint, already decided |
| `client.typed_cell(r, c, s)` | the frame for "the user typed s here" |
| `client.typed_cell(r, c, s, seed)` | the frame for "the user typed s here", or none if `s` is still the `seed` the editor opened on |
| `client.on_key_event(k,c,a,s,m)` | the frame for a keypress, folded locally first where this client may answer it |
**The two rules this file must not break are ordering and framing**: frames are
+83
View File
@@ -4332,6 +4332,15 @@ with no `SetBuffer`, then presses the keys: Enter must compose
`an_edit_buffer_the_user_has_emptied_is_not_refilled_from_the_cell` fails,
as it did against the same rival in `render.rs`.
**Amended by improvise-ixxx.** Its first `Enter` is untouched now, so it
composes no write. The assertion that it "must not clear" is therefore
`committed == None`, where `committed` is the first `Command::Model` of any
kind. It still fails against the pre-ktlk tree:
that tree's buffer is `""`, and `settle_editor` never ran, so no seed was
captured and the empty buffer composes `ClearCell`. The typed-`5` half is
unchanged. `settle_editor` now captures `OpenedEditor` beside the buffer,
because the editor opens at that moment.
## improvise-dsxq — the browser's `yy` copies the value, not a string
PR #161 (improvise-lut7) made the browser's `yank` arm compose
@@ -4491,3 +4500,77 @@ normal edges only, because a dev edge would unify the feature back in
which fails naming that crate while the two tests above pass. Then removed from
`improvise-ws-server` alone, which fails naming it. The native path runs
without `--target`, so the two arms are different invocations.
### `an_untouched_browser_commit_writes_nothing_and_still_advances`
**improvise-ixxx**, browser keyboard. The fixture is a 3×1 grid with the cursor
on row 1. For each seed in `"007"`, `"1e3"`, `"inf"`, `"ERR:boom"` and
`"1.23456789"`, the test folds `i`, checks the buffer is that seed, and resolves
`Enter`. The composed commands must contain no `Command::Model`. They must also
contain `SetSelected { row: 2, col: 0 }`, the re-entry
`ChangeMode(editing)`, and `SetOpenedEditor` capturing `"800"`, the cell it
landed on.
Failed before the fix: `"007"` composed `SetCell(Number(7.0))`. **Rival:**
`resolve::commit` returning `[ChangeMode(Normal)]` when untouched, which skips
the advance along with the write. This test fails alone.
### `an_untouched_browser_edit_does_not_overwrite_a_write_made_while_it_was_open`
The captured-seed constraint, in the browser. The test opens the editor on
`"007"`, then folds a `GridUpdate::Cells` that moves the cell to `"5"`. That is
the projection another client's write produces. `Enter` with nothing typed must
compose no `Command::Model`.
**Rival:** `written` comparing against `edit_seed(state)`, which re-reads the
cache at commit time, instead of `ViewState::opened_editor`. This test fails
alone, composing `SetCell(Number(7.0))`. The basis claim is taken when the
commit is composed, so it would not have refused that write.
### `a_touched_browser_commit_still_writes`
Scope guard. The reviewer's fourth constraint says classification is out of
scope. `Backspace` over the seed `"7"` and then typing `007` must still compose
`SetCell(Number(7.0))`. It passes before and after. It is here so that a fix
which suppressed number-shaped text in general, rather than untouched text,
fails.
### `an_untouched_browser_edit_of_a_misread_value_writes_nothing`
The same shape as `an_untouched_browser_edit_stores_the_identical_value`, with
the values `typed()` misreads: `Text("007")`, `Text("1e3")`, `Text("inf")` and a
raw `Error("boom")`. It runs through the resolver and into a real host. Both
tests share `untouched_browser_edit`, which now also asserts that the buffer
after `i` is `value.edit_text()`. That assertion keeps the umi4 test
discriminating once untouched commits write nothing (see
`command.md`, `an_untouched_pivot_edit_of_a_misread_value_writes_nothing`).
With `project.rs` seeding from `to_string()`, both umi4 browser tests fail.
**Rival:** `untouched` always `false`. This test fails.
### `a_mouse_commit_of_the_untouched_seed_composes_nothing`
**improvise-ixxx**, browser mouse. `web/app.js`'s `edit()` holds `original`,
captured when the editor opened, and used to call `typed_cell` without it. So
an untouched click-edit re-sent the seed through `typed()`. Now
`typed_cell_frame` takes the `OpenedEditor` and returns `None` for
`"007"`, `"ERR:boom"` and `"1.23456789"` given as their own seeds. The control
is `"007"` against a seed of `"7"`, which must compose a write, so a
`typed_cell_frame` that never composes cannot pass.
### `a_mouse_commit_is_judged_against_the_seed_it_opened_with`
The captured-seed constraint, on the mouse path. The editor opened on `"1200"`.
A projected write then moves the cell to `"5"`, and a guard asserts the cache
really holds `"5"`. Committing `"1200"` must compose nothing. **Rival:**
`typed_cell_frame` ignoring the passed seed and comparing against the cache's
`edit_text`. This test and `a_mouse_commit_of_the_untouched_seed_composes_nothing`
both fail.
The page half is `harness/tests/contract.spec.js`, "an untouched mouse editor
sends no write, and a touched one does". It wraps `Worker.prototype.postMessage`,
opens the editor on cell 4, and presses `Enter`. No `SetCell` may be posted.
Then it types `1` and presses `Enter`, and exactly one must be. **Rivals, run
against the served `app.js`:** passing `input.value` as the seed, and passing
no seed at all. Both fail the control with 0 writes. The second fails because
wasm-bindgen throws on the missing `String`, and the page swallows the throw
inside `close`.
+63
View File
@@ -5138,3 +5138,66 @@ exactly that, because West had no record to drill into. It then asserts that
the first message does not mention drilling. At `0ed2e41` it failed with the
old text. The rival is restoring that text. Real drill paste, which means
staging a typed `CellValue`, is still open as improvise-48jd.
### `an_untouched_pivot_edit_of_a_misread_value_writes_nothing`
### `an_untouched_records_edit_of_a_misread_value_writes_nothing`
**improvise-ixxx.** After umi4 the seed was exact, but the commit still ran it
through `typed()`, which reads text and not the cell the text came from. So
`i` then `Enter` with nothing typed stored `Number(7.0)` over `Text("007")`,
and `Text("ERR:boom")` over a raw `Error("boom")`. The values are the ones
where `typed(edit_text(v)) != v`: `"007"`, `"1e3"`, `"inf"`, `"2024"` and a raw
`Error`. They use `no_op_commit`, the umi4 tests' own path: the real key path,
on (West, Revenue), which is row 1 and never the first cell.
Both failed before the fix. With the list as written they failed on
`Text("007")` stored as `Number(7.0)`. With `Error("boom")` moved to the front
they failed on it, stored as `Text("ERR:boom")`, so the error case is red on
its own and not only behind the text one. **Rival:** `untouched` answering
`false` always, i.e. the fix removed. Both fail again.
`no_op_commit` now also asserts that the buffer after `i` is
`value.edit_text()`. **That assertion is what keeps the umi4 tests above
alive.** With ixxx fixed, an untouched commit writes nothing, so a seed
regressed to the rounded `Display` is never written either, and
`an_untouched_{pivot,records}_edit_stores_the_identical_value` pass with umi4
reverted. That was run: with the seed assertion removed and
`display_value_in` seeding from `to_string()`, all six untouched tests pass.
With the assertion in, both umi4 tests fail.
### `an_untouched_edit_does_not_overwrite_a_write_made_while_it_was_open`
The reviewer's first constraint on improvise-ixxx: the seed is the one
**captured when the editor opened**. The test opens the editor on
`Text("007")`, then applies a `SetCell(Number(5.0))` directly, standing in for
another client's write that lands while the editor is open. Then it presses
`Enter` with nothing typed. The cell must still be `5`.
Failed before the fix: the commit wrote `Number(7.0)`. **Rival:**
`commit_at_cursor` taking the seed from `ctx.display_value`, which is
recomputed at commit time, instead of `ctx.opened_editor`. This test fails
alone, with `Number(7.0)`, and every other untouched test passes. So it is the
only guard on the choice of seed, and a fixture where the cell does not move
cannot tell the two seeds apart.
### `an_untouched_commit_still_advances_and_reopens_the_editor`
The reviewer's second constraint: only the **write** is skipped. The fixture
puts `Text("007")` at (West, Cost), row 1 and column 0. `Enter` there advances
to (0, 1) rather than aborting at the corner, which is where (West, Revenue)
sits in pivot mode. The test asserts four things: the cell is unchanged, the
cursor moved to (0, 1), the mode is editing again, and the reopened editor
captured `"2"`, the cell it landed on.
**Rival:** `CommitAndAdvance::execute` returning straight after the mode change
when untouched, which skips the advance along with the write. This test fails,
and so do `tab_on_an_empty_new_measure_column_still_starts_a_record_row` and
`ui::app::tests::tab_on_bottom_right_of_records_inserts_below`. Those two are
the records Tab contract: the empty `+` column's seed is `""`, so an empty Tab
there is now an untouched commit too.
The buffer after `Enter` is **not** asserted. The keymap welds `clear-buffer`
after every commit key, so it empties the buffer `EnterEditAtCursor` has just
seeded, and the reopened editor opens blank while its captured seed is `"2"`.
That is existing behaviour (`enter_commits_the_cell_advances_and_re_enters_editing`
in `clients.md`). It is reported on improvise-ixxx rather than fixed here.