From a69b0bca86a4f704ea6308e559226b8092c13b90 Mon Sep 17 00:00:00 2001 From: Edward Langley Date: Sun, 20 Sep 2026 21:15:49 -0700 Subject: [PATCH] test: the third edit of improvise-0x9u's repair, and two evidence corrections `close` restores the DRAWN cell text rather than the seed, and reverting that one line left the whole suite green while `18400` sat where `18,400` had been on the shipped demo. Asserted after the second click, proved by that revert. improvise-o31u's desktop arm cannot fail for the reason its evidence gave: both named mechanisms were measured and neither moves it (`#cast-pivot` is 966px at 1280, so an 880px floor is inert wherever it is placed). The entry now says it is forward-looking, and credits the phone arm with the fault it actually catches. Plus three one-liners: UNCALLED's source-scan blindness, the gzip -9 proxy in the improvise-cl7y table, and why `view-back` is not one of its five siblings. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH --- context/code/clients.md | 5 +++ context/test-evidence/clients.md | 48 +++++++++++++++++++++--- crates/improvise-protocol/src/resolve.rs | 2 + harness/tests/contract.spec.js | 4 ++ web/tests/bundle/wasm_surface.test.js | 2 + 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/context/code/clients.md b/context/code/clients.md index 4d5b28a9..7760ba91 100644 --- a/context/code/clients.md +++ b/context/code/clients.md @@ -598,6 +598,11 @@ building the bundle both ways: | with `improvise-formula` | 634,515 | 238,973 | | cost | **+130,661 (+25.9%)** | **+47,360 (+24.7%)** | +The raw column is exact and byte-verified; the gzipped one is a `gzip -9` +**proxy** and not what a visitor downloads, since GitHub Pages serves brotli. +The conclusion is unaffected — the raw figure alone carries it — but do not +quote "+47,360 gzipped" as a transfer size. + A quarter of the download, for a status string. That is the evidence for the other route — the host addressing a status back to the composer, the way `ClientFrame::Save`'s rejection already does — and it costs no dependency. diff --git a/context/test-evidence/clients.md b/context/test-evidence/clients.md index 44278561..fea360d2 100644 --- a/context/test-evidence/clients.md +++ b/context/test-evidence/clients.md @@ -2084,6 +2084,26 @@ tell that apart from the original bug — same symptom, different cause — so i the debug run rather than the test that separated them. `close` therefore no longer repaints, and the `blur` arm checks `closed` before dismissing. +Third — and this is the edit the repair nearly shipped unguarded — **the fix is +three edits, not two**, and the third is the one with the most careful comment: +`close` restores `td.textContent` (the *drawn* value) instead of `seed` (the +*stored* one), because `close` no longer repaints and `repaint()` early-returns +for as long as `editing` is set. Reverting that one line to +`td.replaceChildren(original)` left the whole suite green, and the defect was on +the front page: `web/demo.improv` carries `format: ,.0` over `18400`, and this +test's own two clicks — no Escape between them — leave cell 0 reading `18400` +where the page had drawn `18,400`, for as long as the second editor stays open. +The assertion added for it is `toHaveText("18,400")` after the second click, and +the injection is exactly that revert: it fails with `unexpected value "18400"`, +naming this test and no other. + +The pattern is worth carrying past this entry. A repair of N edits whose N−1 +guarded ones pass is not a guarded repair, and the unguarded edit is reliably +the one the author understood best — which is why it carries the clearest +comment and why it felt covered. For each distinct edit, name the test that +fails when only that edit is reverted; a line with a comment and no answer to +that question is the line to inject. + #### "the widgets the demo document fills are drawn, in the state that fills them" The `#doc-name` row is improvise-5jna, and the reason it belongs in a **browser** @@ -2144,12 +2164,28 @@ be the thing that scrolls, which is what separates "contained the overflow" from #### "a desktop viewport is unchanged by the phone fix" -The negative arm, and it is not ceremony: the same media query written without -a `max-width` bound, or with the `min-width` on `.player` itself rather than on -its child, changes the desktop rendering too — and desktop at 13.11px was the -one thing about these players that was already right. It also asserts that a -desktop player has nothing to scroll, so a fix that left the 880px floor in -place at every width would fail here rather than passing both. +The negative arm, and **it is forward-looking: no fault available today makes it +fail.** The entry previously named two mechanisms and both were measured, at a +1280px viewport, against the shipped page: + +- `@media (max-width: 900px)` → `@media all`: this test **passes** (13.1131px, + `scrollWidth === clientWidth`). +- `min-width: 880px` moved from `.player > *` onto `.player`: this test + **passes**; the *phone* arm fails instead, on `scrolls.page === 0`. + +The arithmetic is why, and it is worth stating rather than re-deriving: at +1280px `#cast-pivot` measures `clientWidth === scrollWidth === 966`, so an 880px +floor is inert at desktop wherever it is placed. The floor would have to exceed +the ~966px the player actually gets — or the content column would have to +narrow — before either assertion here could move. That is a real future fault +and the reason to keep the test: it pins 13.11px and "nothing to scroll" against +a floor raised past the player's own width. It is not a reason to claim the arm +currently discriminates between the two placements, because it does not. + +Note also that the **phone** arm is stronger than this entry used to credit it +with: the misplaced-`min-width` fault, attributed here to the desktop arm, is +caught there. `scrolls.page === 0` is the assertion that separates "contained +the overflow" from "moved it onto the document". #### "no cast carries the recording machine's shell, clock or geometry" diff --git a/crates/improvise-protocol/src/resolve.rs b/crates/improvise-protocol/src/resolve.rs index c42644d4..cb894cde 100644 --- a/crates/improvise-protocol/src/resolve.rs +++ b/crates/improvise-protocol/src/resolve.rs @@ -236,6 +236,8 @@ fn declined(name: &str, args: &[String]) -> Option { "toggle-records-mode" => "records mode", "add-record-row" => "adding a record row", "delete-formula-at-cursor" => "deleting a formula", + // NOT "not built": it commits a drill first, so composing it would + // commit another client's typing. Needs addressed commands — clients.md. "view-back" => "stepping back to the previous view", // A door this client will not open still has to say so: entering is // resolvable and the commit behind it is not. diff --git a/harness/tests/contract.spec.js b/harness/tests/contract.spec.js index e7ceee98..06c87b03 100644 --- a/harness/tests/contract.spec.js +++ b/harness/tests/contract.spec.js @@ -237,6 +237,10 @@ test.describe("a pointer event reaches the client and its answer is painted", () await cells.nth(4).click(); await expect(cells.nth(4).locator("input")).toHaveCount(1); await expect(cells.nth(0).locator("input")).toHaveCount(0); + // The third edit of the same repair: `close` restores the DRAWN text, not + // the seed. `repaint` early-returns while `editing` is set, so a `18400` + // put back here stays on screen (improvise-0x9u). + await expect(cells.nth(0)).toHaveText("18,400"); await page.keyboard.press("Escape"); await expect(cells.nth(4)).toHaveClass(/selected/); diff --git a/web/tests/bundle/wasm_surface.test.js b/web/tests/bundle/wasm_surface.test.js index f4016167..b959d77d 100644 --- a/web/tests/bundle/wasm_surface.test.js +++ b/web/tests/bundle/wasm_surface.test.js @@ -89,6 +89,8 @@ test("the page still reaches the client through a handful of calls", () => { /// Methods on `Client` the page does not call, each with the reason it is /// still exported. A door with no caller and no entry here is improvise-seik: /// the dangerous kind, because calling the wrong one of a pair is silent. +// What this CANNOT see, exactly as UNPAINTED cannot: `callsOn` is a source +// scan, so a `client.foo()` inside a function nothing calls reads as called. const UNCALLED = { free: "wasm-bindgen's own destructor, not ours to remove", initSync: "wasm-bindgen's own module loader, beside the default export",