Repository navigation
ci(windows): run the Claude command history tests on native Windows - #2621
Conversation
Its same-size-rewrite case failed 13 of 20 runs on native Windows, where two writes a millisecond apart keep a file's id, size and timestamps, and no Windows job ran the file.
|
No actionable comments were generated in the recent review. Summary
Merge Risk:
|
Deploying ovis with
|
| Latest commit: |
8daa2cc
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://d082a7f5.ovis.pages.dev |
| Branch Preview URL: | https://ci-windows-command-history-t.ovis.pages.dev |
…2684) ## Change `scripts/chat-selection-turn-end-demo-regression.ts` (#2658) failed intermittently in "Browser regression (other scripts)" on PRs that do not touch chat, e.g. #2621 run 41725988136: `desktop-1280-fold`, one frame at 533.3 px against 572.8. It is not the test: the selection hold from #2658 loses the words under the selection when frames are slow. Instrumenting every frame (rAF, every ResizeObserver callback, every `scrollTop` set and `scrollBy`, the scroll events) on a slow machine showed three races, all in the hold: 1. **A fold in the held words' own row was painted uncorrected for a frame.** LegendList puts a row's shrink of more than 1 px off to the next animation frame (`scheduleWebShrinkMeasurement`), so when the ending turn's work block folds above the words in the same row, no observer the hold listens to fires in that frame. On a slow frame the slide moves 40-50 px at once, and that frame is painted with the words moved. The hold now also watches the held words' own row (`[data-chat-row]`) with the chat's resize observer, which the chat made when it mounted, so the move is put back in the frame it happens, before the paint. 2. **The hold's own correction made the chat think the reader left the end.** `keep()` set a fractional `scrollTop` (253.5625), reported the read-back (254) through `onFollowed`, and the scroll event then read 253: a 1 px move up with no input, which `ChatView.onScroll` took for the reader leaving the end. `following` went false, `keep()` stopped holding, and the rest of the fold drifted the words 10-27 px. `keep()` now scrolls in whole pixels, and keeps the words' aimed place as its reference instead of re-reading it, so the rounding adds up with the next move instead of drifting by a pixel. 3. **The list and the browser move the view up after the hold, with no input.** A frame later LegendList measures the same fold and its MVCP `scrollBy`s again (−24, −36 px), and the browser trims the view as the transcript shrinks. `ChatView.onScroll` now takes a move up without the reader's input as the layout's while a selection holds the view (the reader's wheel, keys, touch and scrollbar still count as leaving), and the hold hears the list's adjust (`listen("scrollAdjust")`) and puts the words' whole move back, as it already did for the list's padded frame (`listEndPadded`). `scrollAdjust` is a signal LegendList's `listen` already delivers, but its public `LegendListListenerType` leaves it out; the existing `patches/@LegendApp%2Flist@3.3.5.patch` gains a one-line `react.d.ts` hunk adding it (no runtime change, `bun.lock` unchanged; checked with a clean install from an empty cache). The test: readings are compared unrounded. Rounding each to 0.1 px first made a 0.96 px step (inside the hold's own sub-pixel rule, put back the next frame) read as exactly 1 px and fail `< 1`. And a new case, `desktop-1280-fold-reduced-motion`: with reduced motion the block folds in one frame, which is what one slow frame of the slide does, so it fails on main every time instead of only on a slow runner (on main the words move to 608.8 and then 460.8 px; reduced-motion users saw that). Review round (1db6fa9): only a move up of at most one pixel without input is excused while a selection stands (the browser's whole-pixel trim), only a move of at most a pixel landing within a pixel above where the chat itself last put the view (a4e9fa8, 8181b42), so a reader who scrolls up with no input the page sees, in large steps or a pixel at a time, is still away from the end once the selection goes; the script gains `desktop-1280-reader-away` for it (fails 3/3 on c9901b9), and watches each fold until it has slid shut. Details in the review comment. No changelog fragment: `changelog.d/chat-selection-holds-still.md` from #2658 is still unreleased, and this makes it true. ## Validation Before/after on this PC (load average 36-98 from other lanes and CI runners), the demo build as CI builds it (`bun scripts/demo-build.ts`), through `dori heavy`: | | origin/main 4bc043d | this PR | | --- | --- | --- | | `chat-selection-turn-end-demo-regression.ts`, whole script | 0/20 pass (old script); 0/5 with the new script, the reduced-motion case failing every time it is reached | 24/24 pass, 6 in parallel | | `desktop-1280-fold` only, looped (instrumented copy) | fails within the first 3 iterations | 150/150 (6 × 25 in parallel) | | `phone-390-fold` only, looped | | 25/25 | | `desktop-1280-fold` with reduced motion, looped | 0/5 | 10/10 | Intermediate states, for the record: with only the row watch (1), 18/20 whole-script runs passed and the remaining failures were the drift of (2) and (3); with (1) and (2) but no list-adjust signal, transitions passed 150/150 but reduced motion still failed. - `bun run check fast` (through `dori heavy`) on c9901b9 and again on 1db6fa9: workflow syntax, generated types and themes, typecheck, build, unit tests (10883 pass) all ok. - On 1db6fa9, under the same load: whole script (then 7 cases) 24/24; `desktop-1280-fold` looped 150/150, `phone-390-fold` 25/25, reduced motion 10/10. - On a4e9fa8 (last review round: only a landing a pixel above where the chat last put the view is excused), whole script (10 cases: reader-away in 30 px steps, 1 px steps, 1 px after growth below, and down-then-up) 24/24; the same loops 150/150, 25/25, 10/10; the 11 adjacent scripts pass; `check fast` ok. Each new reader-away case fails on the commit before its fix (1db6fa9, c358899, f37fab8). On the merged head 0830c48: `check fast` ok (10894 unit tests), whole script 6/6. On 8181b42 (the event's own move must be a pixel at most too; new 151 px case, which fails 2/2 on 0830c48): whole script (11 cases) 6/6, `check fast` ok. - Adjacent browser scripts on the final build, all pass: `chat-virtual-browser-qa.ts`, `chat-history-browser-qa.ts`, `chat-local-send-follow-demo-regression.ts`, `chat-quote-selection-end-regression.ts`, `chat-scroll-padding-regression.ts`, `chat-select-quote-demo-regression.ts`, `chat-selection-copy-regression.ts`, `chat-working-line-demo-regression.ts`, `chat-no-rewind-demo-regression.ts`, `reply-comments-demo-regression.ts`, `chat-gjc-streaming-browser-qa.ts`. - `chat-browser-qa.ts` could not run here: it times out waiting for `.conn-live` on this PC, the same on origin/main (no live connection in this environment); CI runs it. - `chat-virtual-browser-qa.ts`'s failure on #2644 (`continuous content left the reader behind for 6 consecutive animation frames`, line 717) is a different cause: it is about following streamed content, and that run (2026-11-03 08:03 UTC) predates #2658. Not addressed here. - Not run: `bun run check full`, the Mac and Windows lanes. No server, shared or install code changed. --------- Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
…d chat lands at its end (#2720) ## Change `scripts/chat-virtual-browser-qa.ts:81` (`settleBottom` after `page.reload()`) timed out intermittently on PR runners (#2621's rerun, twice in a row; #2644): the reopened chat ended about 54 px short of its end, and CI's failure screenshot shows Jump to latest. The test was right: a reopened chat took the reader for one who had scrolled away, with no input. Caught with a debug build that logs every follow and scroll decision (it failed the first reload in 4 of 8 runs under load): 1. On the reload the list's first landing is short of the end (its footer not measured yet), and the chat's `followMeasured` puts the view at the real end: `max(measured 120061, scrollHeight 120115) - 900 = 119215`. 2. In the same frame the view is pulled up to **119161 = the list's own measured `contentLength` (120061) - clientHeight**. LegendList's own scroll to the end is still on its way, and while one is (`state.scrollingTo`), its `onScroll` clamps any position past the end *it has measured so far* back to that end (`clampScrollOffset`). Its measure lags the transcript by 54 px there (the footer and some rows not measured yet). 3. `ChatView.onScroll` sees a move up with no input, 54 px from the end (more than the 48 px layout allowance) over a transcript that did not shrink, and concludes the reader left the end: `stick=false paging=true`, Jump to latest. Nothing follows again. When the lag is under the allowance, the chat instead stays "following" short of the end, with nothing left to correct it (the local variant). LegendList's end on the web can only lag what the browser shows (it measures what it renders, a frame or more late), so the fix is in the LegendList patch this repo already keeps (`patches/@LegendApp%2Flist@3.3.5.patch`, two hunks, plus a helper): - A helper finds the end of what is drawn: the bottom of the list content's children and of the rows inside the rows' wrapper (rows as tall as they are drawn, a header or footer not measured yet), relative to the scroll origin. It deliberately leaves out the room the list pads past its end for a frame while it holds rows in place, and a `min-height` under the content (the selection hold's room, #2684). An earlier version used the scroller's `scrollHeight`, which includes that room, and made `reply-comments-demo-regression.ts` drag selections land on other words in 2 of 7 runs. - `clampScrollOffset` never clamps below that drawn end. This is what pulled the view up in the captured failure. - `scrollToEnd` adds what the list has not measured yet (the drawn end past its measured one) to its end offset, so its own target is the real end instead of re-landing short. When the list's measure matches what is drawn (settled), both changes are no-ops; a larger measure (rows estimated taller than drawn) keeps the list's own value. No app code changes. The test: `chat-virtual-browser-qa.ts` gains the case right after the reload step that timed out. A 100 px box drawn after the footer inside the list's content, which the list never measures, makes its end lag the transcript's by more than the layout allowance (as an unmeasured footer and rows did in the captured failure), and a view a pixel shorter makes the chat follow its end and the list scroll to its own. The chat must end at the end, still following, with no Jump to latest. On main it ends 61 px short in every frame from the second on (`{"gaps":[101,100,61,61,...]}`); with this change it ends at 0. ## Validation - `chat-virtual-browser-qa.ts` with the new case: origin/main (with #2684) **fails 2/2** at the new case (61 px short in every frame from the second on); this branch **passes 4/4** (all four width/theme passes). The case lags the list's end twice: a box after the footer, and a box past the rows' wrapper (a row drawn taller than measured), which the review head 735a121 failed 2/2. - `reply-comments-demo-regression.ts` (drag selections, which the earlier `scrollHeight` version broke): 10/10 on main; on this branch 10/10, and 6/6 again on the final head 74b4389 (with turn-end 4/4, chat-history and local-send-follow passing, `check fast` ok). - The original reload timeout reproduces only on a loaded machine (about once in a few hundred reloads on this PC, 2 of 2 attempts on #2621's runner), so its before/after rests on the captured trace above and the deterministic case. - Merged with main (#2684's selection hold also leans on the list's scroll): `chat-selection-turn-end-demo-regression.ts` 6/6 on this branch. - Adjacent browser scripts on this branch, all pass: `chat-history-browser-qa.ts`, `chat-local-send-follow-demo-regression.ts`, `chat-quote-selection-end-regression.ts`, `chat-scroll-padding-regression.ts`, `chat-select-quote-demo-regression.ts`, `chat-selection-copy-regression.ts`, `chat-working-line-demo-regression.ts`, `chat-no-rewind-demo-regression.ts`, `reply-comments-demo-regression.ts`, `chat-gjc-streaming-browser-qa.ts`. Runs were capped at 4 at a time with one shared demo build per variant. - The patch applies with a clean `bun install` from an empty cache and reproduces the tested file byte for byte; #2684's `react.d.ts` hunk is kept; `bun.lock` is unchanged. - `bun run check fast`. - Not run: `bun run check full`, the Mac and Windows lanes (no platform code changed). Changelog: `changelog.d/chat-reopen-lands-at-end.md` (Fixed). --------- Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
* fix(qa): open the folded work run before claude-image clicks a row A work block's calls fold into a run line, so the row the check clicks is not drawn until the run is opened (the same step #44 added to the other image checks). * fix(qa): hide-pc waits for motion that ends, not an agent's wave An agent friend's wave never ends, so waiting for every animation to stop timed out. A looping animation is no motion to wait for. * fix(qa): terminal-autoswitch reads the lens the header shows The header's lens button flips the lens with one tap (#49) and shows the one it goes to, so a peek's header shows the grid, and one tap on it ends the peek. The check no longer expects Chat pressed, and the second way of keeping the terminal is the same tap as the first. * fix(qa): workspace-rows reads the tab strip's row surface The strip's background is on .tab-strip-row now; .tab-strip itself is transparent. * fix(qa): pane-mute fixture's roster tells each pane's last status A roster carries each pane's status as the server's does, so a page that takes a roster again (a stream reopened as the tab is shown) finds the panes as the frames left them, instead of the demo's first statuses. * fix(chat): keep the words under a selection held through a slow fold (devswha#2684) ## Change `scripts/chat-selection-turn-end-demo-regression.ts` (devswha#2658) failed intermittently in "Browser regression (other scripts)" on PRs that do not touch chat, e.g. devswha#2621 run 41725988136: `desktop-1280-fold`, one frame at 533.3 px against 572.8. It is not the test: the selection hold from devswha#2658 loses the words under the selection when frames are slow. Instrumenting every frame (rAF, every ResizeObserver callback, every `scrollTop` set and `scrollBy`, the scroll events) on a slow machine showed three races, all in the hold: 1. **A fold in the held words' own row was painted uncorrected for a frame.** LegendList puts a row's shrink of more than 1 px off to the next animation frame (`scheduleWebShrinkMeasurement`), so when the ending turn's work block folds above the words in the same row, no observer the hold listens to fires in that frame. On a slow frame the slide moves 40-50 px at once, and that frame is painted with the words moved. The hold now also watches the held words' own row (`[data-chat-row]`) with the chat's resize observer, which the chat made when it mounted, so the move is put back in the frame it happens, before the paint. 2. **The hold's own correction made the chat think the reader left the end.** `keep()` set a fractional `scrollTop` (253.5625), reported the read-back (254) through `onFollowed`, and the scroll event then read 253: a 1 px move up with no input, which `ChatView.onScroll` took for the reader leaving the end. `following` went false, `keep()` stopped holding, and the rest of the fold drifted the words 10-27 px. `keep()` now scrolls in whole pixels, and keeps the words' aimed place as its reference instead of re-reading it, so the rounding adds up with the next move instead of drifting by a pixel. 3. **The list and the browser move the view up after the hold, with no input.** A frame later LegendList measures the same fold and its MVCP `scrollBy`s again (−24, −36 px), and the browser trims the view as the transcript shrinks. `ChatView.onScroll` now takes a move up without the reader's input as the layout's while a selection holds the view (the reader's wheel, keys, touch and scrollbar still count as leaving), and the hold hears the list's adjust (`listen("scrollAdjust")`) and puts the words' whole move back, as it already did for the list's padded frame (`listEndPadded`). `scrollAdjust` is a signal LegendList's `listen` already delivers, but its public `LegendListListenerType` leaves it out; the existing `patches/@LegendApp%2Flist@3.3.5.patch` gains a one-line `react.d.ts` hunk adding it (no runtime change, `bun.lock` unchanged; checked with a clean install from an empty cache). The test: readings are compared unrounded. Rounding each to 0.1 px first made a 0.96 px step (inside the hold's own sub-pixel rule, put back the next frame) read as exactly 1 px and fail `< 1`. And a new case, `desktop-1280-fold-reduced-motion`: with reduced motion the block folds in one frame, which is what one slow frame of the slide does, so it fails on main every time instead of only on a slow runner (on main the words move to 608.8 and then 460.8 px; reduced-motion users saw that). Review round (1db6fa9): only a move up of at most one pixel without input is excused while a selection stands (the browser's whole-pixel trim), only a move of at most a pixel landing within a pixel above where the chat itself last put the view (a4e9fa8, 8181b42), so a reader who scrolls up with no input the page sees, in large steps or a pixel at a time, is still away from the end once the selection goes; the script gains `desktop-1280-reader-away` for it (fails 3/3 on c9901b9), and watches each fold until it has slid shut. Details in the review comment. No changelog fragment: `changelog.d/chat-selection-holds-still.md` from devswha#2658 is still unreleased, and this makes it true. ## Validation Before/after on this PC (load average 36-98 from other lanes and CI runners), the demo build as CI builds it (`bun scripts/demo-build.ts`), through `dori heavy`: | | origin/main 4bc043d | this PR | | --- | --- | --- | | `chat-selection-turn-end-demo-regression.ts`, whole script | 0/20 pass (old script); 0/5 with the new script, the reduced-motion case failing every time it is reached | 24/24 pass, 6 in parallel | | `desktop-1280-fold` only, looped (instrumented copy) | fails within the first 3 iterations | 150/150 (6 × 25 in parallel) | | `phone-390-fold` only, looped | | 25/25 | | `desktop-1280-fold` with reduced motion, looped | 0/5 | 10/10 | Intermediate states, for the record: with only the row watch (1), 18/20 whole-script runs passed and the remaining failures were the drift of (2) and (3); with (1) and (2) but no list-adjust signal, transitions passed 150/150 but reduced motion still failed. - `bun run check fast` (through `dori heavy`) on c9901b9 and again on 1db6fa9: workflow syntax, generated types and themes, typecheck, build, unit tests (10883 pass) all ok. - On 1db6fa9, under the same load: whole script (then 7 cases) 24/24; `desktop-1280-fold` looped 150/150, `phone-390-fold` 25/25, reduced motion 10/10. - On a4e9fa8 (last review round: only a landing a pixel above where the chat last put the view is excused), whole script (10 cases: reader-away in 30 px steps, 1 px steps, 1 px after growth below, and down-then-up) 24/24; the same loops 150/150, 25/25, 10/10; the 11 adjacent scripts pass; `check fast` ok. Each new reader-away case fails on the commit before its fix (1db6fa9, c358899, f37fab8). On the merged head 0830c48: `check fast` ok (10894 unit tests), whole script 6/6. On 8181b42 (the event's own move must be a pixel at most too; new 151 px case, which fails 2/2 on 0830c48): whole script (11 cases) 6/6, `check fast` ok. - Adjacent browser scripts on the final build, all pass: `chat-virtual-browser-qa.ts`, `chat-history-browser-qa.ts`, `chat-local-send-follow-demo-regression.ts`, `chat-quote-selection-end-regression.ts`, `chat-scroll-padding-regression.ts`, `chat-select-quote-demo-regression.ts`, `chat-selection-copy-regression.ts`, `chat-working-line-demo-regression.ts`, `chat-no-rewind-demo-regression.ts`, `reply-comments-demo-regression.ts`, `chat-gjc-streaming-browser-qa.ts`. - `chat-browser-qa.ts` could not run here: it times out waiting for `.conn-live` on this PC, the same on origin/main (no live connection in this environment); CI runs it. - `chat-virtual-browser-qa.ts`'s failure on devswha#2644 (`continuous content left the reader behind for 6 consecutive animation frames`, line 717) is a different cause: it is about following streamed content, and that run (2026-11-03 08:03 UTC) predates devswha#2658. Not addressed here. - Not run: `bun run check full`, the Mac and Windows lanes. No server, shared or install code changed. --------- Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com> (cherry picked from commit e96b369) * fix(qa): pane-mute waits for the sheet's focus and ignores a cancelled animation CI runs the script with UI_EVIDENCE_DIR set, which adds the captures. In them the phone's keyboard walk pressed ArrowDown before the opening sheet had taken focus, so the arrows landed on the toggle behind it and the focus ended on the sheet's first item: the check now waits for focus to be inside the sheet first, as the desktop branch does for the menu. capture() also awaited animation.finished bare, which rejects with an AbortError when an animation is cancelled; the other settle helpers catch that, and so does this one now. --------- Co-authored-by: Hako <devswha@gmail.com> Co-authored-by: sisyphus-dev-ai <sisyphus-dev-ai@users.noreply.github.com>
Owner approved 2026-11-04.
Change
Adds
./server/claude-command-history.test.tsto thebun testlist of theNative Windows installjob in.github/workflows/ci.yml(one line). Its same-size-rewrite case failed 13 of 20 runs on native Windows on main (38230517), where two writes a millisecond apart keep a file's id, size and timestamps, and no Windows job ran the file. The product fix is #2620. #2620 has merged, and this branch now includes it (merged origin/main).Validation
actionlint 1.7.12on.github/workflows/ci.yml: ok.c16d0c9erun in fix(claude): recheck a same-size history rewrite within one timestamp tick #2620).bun run check fastafter merging origin/main: ok (unit: 10883 pass, 0 fail, 41 skip).