Repository navigation
webviews: a ratchet refuses new fixed-time waits in tests - #23365
Conversation
…soned marker Fails until scripts/check-fixed-waits.ts exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue
check:fixed-waits (in `bun run check`) names each test wait for a fixed number of ms: page.waitForTimeout, Bun.sleep, a setTimeout that resolves a promise after a non-zero delay, and calls to a local helper built on one. A fixed wait passes on an idle machine and fails on a loaded runner that wakes it late, or hides the bug it waits out (#23323). A test waits for a condition instead, uses fake timers when the timing is the behavior, or keeps the wait with a `// fixed-wait: <reason>` comment. The 398 waits from before the rule have per-file counts in scripts/fixed-waits-baseline.tsv. A file fails with more unmarked waits than its row, and with fewer until the row is lowered (--update-baseline), so the count only moves down; the sweep PRs lower it. pageSwitch's 16 ms no-requestAnimationFrame fallback keeps its wait with a reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Gallery before/after for
States
Pick other states with a Yours to fix: CI failed on
Failures:
Matched log linesNot re-run automatically: Written by red: web / Web status; base red: the same failure on the base 46b0b80; does not block
Agents: Tour of |
This comment has been minimized.
This comment has been minimized.
…imers and exported helpers Fails on the first detector: a helper with no delay parameter counted only at its definition, a helper passed as a value (act(settle)) not at all, a timer callback with a block body was missed, and so was an exported helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue
From review. A wait inside a named function makes it a helper, and each use
counts instead of the definition: a call, or the helper passed or returned as a
value (act(settle), return { open }), applied outward through helpers built on
helpers. So another `await settle()` in a file raises its count. A helper whose
delay parameter defaults to 0 waits only when a use passes a delay. An exported
helper also counts at its definition, since its callers are in other files.
Also caught: timer callbacks with a block body that resolve the promise,
window/globalThis.setTimeout and Bun.sleepSync. A marker counts anywhere in the
wait's statement, or on a comment-only line above it, so a trailing marker no
longer covers the next line. The scan covers every source under test/
(harnesses, latency helpers), not just *.test files, and skips fixtures. A
malformed baseline row now fails instead of passing as NaN.
The baseline is regenerated: 819 waits in 132 files.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue
|
Bead |
…ixed-wait count Review of #23365 found that a second wait or helper call added inside a function that already waits was not counted. These cases fail on the current detector. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue
…lpers too A wait counted only through the outer uses of its enclosing helper, so a new wait or helper call inside a helper that already waited changed nothing. Now each unmarked wait site counts, and each use of a helper outside its own body, so any new wait raises the count. Waits in exported helpers count at the site, which replaces the export rule. Export lists, typeof and member declaration names are no longer uses. The baseline is regenerated: 989 waits in 132 files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue
Under CMUX_NEXT_STYLE_GUARDS=warn the webviews-check step runs bun run check as separate commands, so check:fixed-waits never ran there. It fails the step, like check:browser-lanes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue
|
Review (subagent): SHIP at 1af8f01 Remaining reds also fail without this PR: |
…ase's new ratchets check:fixed-waits (#23365) and check-frame-reads joined the base's web lint after this branch's tests were written; each wait and the sweep's frame read says why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray
|
base-red-watch: |
…23414) #23374 merged after the fixed-wait ratchet (#23365) ran CI, and it added a 3 s waitForTimeout in two tests, which puts feat-cmux-next over its baseline. Both waits are observation windows: the test records every painted frame for 3 s after the chat opens and asserts none jumped, so the time is the test. They carry a fixed-wait marker instead of a raised baseline. Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…times The fixed-wait ratchet (#23365) counts the waits these new cases added. They now wait for painted frames, the scroller's offset to rest, the view's resize and the queued card or reply text to show. Calls to the files' shared open() and stream() helpers say why they stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray
… a timer The fixed-waits ratchet (#23365): wheelAt counts animation frames until the transcript and the stack's scrollers hold still for 12 frames, or 30 frames pass with nothing moved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray
… 500 ms Base's fixed-wait ratchet (#23365) counts agent-pane-step-rows.test.ts at its baseline again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray
Base's fixed-wait ratchet (#23365) counts agent-pane-agents-pill.test.ts at its baseline again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray
|
Shipped in:
|
…23415) * test(webviews): a fixed-wait check names fixed waits and honors a reasoned marker Fails until scripts/check-fixed-waits.ts exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * webviews: a ratchet refuses new fixed-time waits in tests check:fixed-waits (in `bun run check`) names each test wait for a fixed number of ms: page.waitForTimeout, Bun.sleep, a setTimeout that resolves a promise after a non-zero delay, and calls to a local helper built on one. A fixed wait passes on an idle machine and fails on a loaded runner that wakes it late, or hides the bug it waits out (#23323). A test waits for a condition instead, uses fake timers when the timing is the behavior, or keeps the wait with a `// fixed-wait: <reason>` comment. The 398 waits from before the rule have per-file counts in scripts/fixed-waits-baseline.tsv. A file fails with more unmarked waits than its row, and with fewer until the row is lowered (--update-baseline), so the count only moves down; the sweep PRs lower it. pageSwitch's 16 ms no-requestAnimationFrame fallback keeps its wait with a reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): the fixed-wait check counts helper uses, block-body timers and exported helpers Fails on the first detector: a helper with no delay parameter counted only at its definition, a helper passed as a value (act(settle)) not at all, a timer callback with a block body was missed, and so was an exported helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * webviews: the fixed-wait check counts every use of a sleep helper From review. A wait inside a named function makes it a helper, and each use counts instead of the definition: a call, or the helper passed or returned as a value (act(settle), return { open }), applied outward through helpers built on helpers. So another `await settle()` in a file raises its count. A helper whose delay parameter defaults to 0 waits only when a use passes a delay. An exported helper also counts at its definition, since its callers are in other files. Also caught: timer callbacks with a block body that resolve the promise, window/globalThis.setTimeout and Bun.sleepSync. A marker counts anywhere in the wait's statement, or on a comment-only line above it, so a trailing marker no longer covers the next line. The scan covers every source under test/ (harnesses, latency helpers), not just *.test files, and skips fixtures. A malformed baseline row now fails instead of passing as NaN. The baseline is regenerated: 819 waits in 132 files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): a new wait inside an existing helper must raise the fixed-wait count Review of #23365 found that a second wait or helper call added inside a function that already waits was not counted. These cases fail on the current detector. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * fix(webviews): count every fixed wait and every helper use, inside helpers too A wait counted only through the outer uses of its enclosing helper, so a new wait or helper call inside a helper that already waited changed nothing. Now each unmarked wait site counts, and each use of a helper outside its own body, so any new wait raises the count. Waits in exported helpers count at the site, which replaces the export rule. Export lists, typeof and member declaration names are no longer uses. The baseline is regenerated: 989 waits in 132 files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): U2 waits for conditions, not fixed times The 19 U2 unit-test files had 142 fixed waits under the stricter detector; none are left and their baseline rows are gone. - Timing under test runs on bun's fake timers: the new tab's picked-name fade, the summary card's running-time tick, the kept-attachments rest timer, the transcript's anchor settle, the tooltip's scroll quiet time (now also checked inside the quiet window) and the link bubble's no-hover-card check. - Polls that already waited on a condition (render card, dropped chips, KaTeX, prompt accepted) yield a macrotask per try instead of sleeping. - The image viewer's actions wait for the host request (or the refusal label); the changelog pages wait for their release to draw. - Menu and composer settle() helpers and PreviewCard's teardown yield one macrotask: act() flushes the rest, and the 20 ms were slack. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): yield a task before the no-bubble and one-request asserts The hover test now lets promises and a later task run before it asserts no bubble, and the image viewer waits one more task so a duplicate request would show. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): mark the two 3 s frame-watch windows as fixed waits #23374 merged after the fixed-wait ratchet (#23365) ran CI, and it added a 3 s waitForTimeout in two tests, which puts feat-cmux-next over its baseline. Both waits are observation windows: the test records every painted frame for 3 s after the chat opens and asserts none jumped, so the time is the test. They carry a fixed-wait marker instead of a raised baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): lower the fixed-wait baseline after the base merge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s the transcript (#23243) * test(agent pane): the gutter beside every part of the floating stack scrolls the transcript A browser test (Chromium and WebKit) with one chat that shows every part of the bottom stack at once: the question, the pull request dock, an error card, the agents pill, the queue and the composer's card, then the dictation notice. Beside each part's card the pointer reaches the transcript and the wheel scrolls it; over the card it does not. Red on this head: the question, pull request dock, error card and agents pill bands take the pointer across the pane's width. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * test(agent pane): the dock-gutters chat opens on its stack, and counts only drawn parts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * fix(agent pane): the gutter beside every part of the floating stack scrolls the transcript The floating bottom stack passed the pointer through only around the composer's card: its other parts (the question card, the pull request dock, error cards, the agents pill) are bands the pane's width wide with their card inset, and the band took the pointer, so the wheel in the gutter beside them scrolled nothing. Now every part's band passes the pointer through and what it holds takes it, the rule the composer had, so a new part gets it too. Text-only parts and notice lines keep the whole band. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * test(agent pane): the stack's bands take the pointer, and the wheel in their empty room scrolls the transcript Review of #23243: with the bands passing the pointer through, every empty spot in a band (right of the agents pill, right of a pull request's chip, its padding) reached the transcript rows hidden under the stack, so hover, click and drag acted on unseen rows. The test now asks that those spots reach the band while the wheel there still scrolls the transcript, and that a part scrolling in a short pane scrolls alone with its scrollbar under the pointer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * fix(agent pane): the stack's bands take the pointer and send the wheel on to the transcript Review of #23243: the transcript's rows under the floating stack are only masked, so a band that passed the pointer through let a hover, click or drag in its empty room reach rows no one sees. The bands take the pointer again (the composer still passes it through around its card), and wheelPassThrough.ts forwards a wheel over a band itself to the transcript, as it does over Jump to latest, unless the band scrolls itself; the pull request dock's and question card's scrollbars take the pointer again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * test(agent pane): the short-pane case asks the pull request dock to scroll alone; the question card only keeps the transcript still Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * test(agent pane): the dock-gutters wheel waits for the transcript to hold still before the next measure Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * test(agent pane): the dock-gutters wheel waits on painted frames, not a timer The fixed-waits ratchet (#23365): wheelAt counts animation frames until the transcript and the stack's scrollers hold still for 12 frames, or 30 frames pass with nothing moved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* test(webviews): base passes the fixed-wait ratchet again #23094 merged after the guard (#23365) with 9 new fixed waits, so check:fixed-waits fails on base and on every PR into feat-cmux-next. - snapshot-save-throttle: the 250 ms save timer runs on bun's fake timers; nothing is written at 249 ms, one write at 250 ms. (The old 300 ms sleep passed with any delay under 300 ms.) - streaming-text harness and frames test: the reveal drain and the settle window are sampled, so the windows are the test; the 500 ms before sampling has no start-up-done signal. Marked fixed-wait. - RevealedMarkdown: the 20 ms in afterEach is teardown with no assert after it. Marked fixed-wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): reveal-message runs its row wait on the fake clock #22967 added three fixed waits after the guard: the wait for a revealed row to be drawn polls on setTimeout, so the three tests that watch it now run on bun's fake timers and advance the clock instead of sleeping. Breaking the poll, the stale-chat check or the cancel of an earlier mark each fails its test. Also: the frames test's start-up comment no longer guesses the cause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): queued-images waits for snapshots; a deleted test's baseline row passes #22975 merged two unmarked polls in agent-pane-queued-images (CI ran before the fixed-wait guard); they now wait on the client's snapshots. #23339 deleted session-sidebar-preview.test.tsx while it still had a baseline row, so the check failed every PR; a row whose file is gone now passes and drops on --update-baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): drop the deleted test's baseline row; queued-images' wait fails before bun's timeout Review nits on #23452: the stale session-sidebar-preview row would have let a new file at that path keep 6 waits, and a 5 s condition timeout lost its message to bun's 5 s test timeout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…e the composer; a finished subagent leaves one line (#23104) * cmux NEXT: running subagents sit in one tray above the composer; a finished one leaves one line Leo: "put the subagent situation at the bottom of the chat and anchor". A running subagent drew a "1 subagent running" card mid-transcript where it started, a separate "1 agent" chip sat by the composer, and the summary popover used a third style. Now every running subagent and background command is a line in one tray in the bottom stack, right above the composer (name, what it does now, elapsed, state, theme-accent icon, stop button). Several get a header; past three they fold. The transcript keeps no running card: an ended subagent leaves one line where it ended, "Subagent finished: <name> · 1m 20s", a failure in the danger color. The tray's line is the summary card's row (AgentLine). The chip is gone. Debug Settings agentPane.debug.subagentTray (default on, sent with the theme) keeps the old group card and pill when off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * cmux NEXT: the subagent end lines' translations are reviewed Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * cmux NEXT: the tray is the one place running background work shows The tray above the composer now lists background commands from summary/backgroundWork.ts (acpmux's process registry merged with the transcript's tasks) and the chats the agent started, in a turn and between turns. Between turns its header says "Running in background · 1 agent · 2 commands · 1 session". A registry command shows whenever the registry says it runs, with Stop. A command only the transcript knows shows only while a turn runs, so a peer's chat whose task end never arrived no longer keeps it up between turns. The transcript's "Running in background" row (BackgroundWorkRow.tsx) is gone, and with the tray on the running line counts nothing. With Debug Settings agentPane.debug.subagentTray off, the line keeps its counts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * cmux NEXT: the running tray's clock rests while the pane is hidden useNow (the tray's, the group card's and the summary rows' one-second clock) stops ticking while the page is hidden and catches up the moment it shows. In a short pane the tray gives up its height and scrolls in place, like the other parts of the bottom stack. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * Tray highlight: shorter summary and a docs link Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * test(webviews): the tray clock test moves time by hand instead of waiting The hidden-pane clock test recorded a 1.3 s real wait. It now records the clock's interval instead of running it and sets the time with setSystemTime, so it checks the same rest and catch-up with no wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * cmux NEXT: the tray's header time is its own; a quiet chat lists no processes The tray header's elapsed time carried the lines' time class, so a tray with two lines counted three line times (agent-pane-agents-pill.test.ts). It now has its own class, acpmux-running-tray__time. Reading acpmux's process registry whenever the tray was on sent _acpmux/process/list from every chat, a method the host's pane policy does not allow (agent-pane-session-pages.test.ts). The registry is read again only while the summary card shows, a turn runs or a command is known to run, as before. The tray still shows a registry command between turns while the registry reports it running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * Expect "Working for" on the running line with the tray on Base #23164 made the running line read the turn's time; the read in flight has its own tool row. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * Drop the duplicate test registration from the base merge Base registered tests/test_guard_hooks_from_base.py too (#23349), so the merge kept it twice and the registry validator refused it. Also point the backgroundSection doc at the callers that still exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * test(subagent tray): wait for the tray to detach, not a 100 ms poll Base's fixed-wait ratchet (#23365) counts agent-pane-agents-pill.test.ts at its baseline again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * gallery(subagent tray): several-running shows three open lines Four items start folded (TRAY_OPEN_MAX = 3), so the play's wait for four lines never held and gallery health reported the state broken. Two running subagents and a command keep it open; many-folded covers the fold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
) * cmux NEXT: tests for one quiet 'Thought for 12s' row per finished thought (red) A short finished thought still reads 'Thought <its text>' inline, a quiet one still draws, and consecutive thoughts stay two rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * cmux NEXT: a finished thought is one quiet 'Thought for 12s' row Every finished thought folds to "Thought for 12s" with none of its text inline (Debug choice agentPane.debug.thoughtFoldThreshold "every", the new default; "viewport" and "codex" keep the older fold of long thoughts only). Consecutive thoughts in one step merge into one for their summed time, and an empty thought, or one under 40 characters done in under a second, draws no row; a step of only such thoughts is left out. The fold toggle is now on unless the app sets data-fold-long-thoughts="off", so the gallery and dev server draw the shipped default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * cmux NEXT: the thought-rows highlight links its docs page Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * cmux NEXT: thought gallery plays allow the opened text's push, and find the quiet step by its calls Opening a folded thought moves the rows below it on purpose (layout shift 0.0048 and 0.0021 in CI). The quiet play waited for 'Read client.ts', which a live run that shows only its newest call never draws. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * cmux NEXT: the quiet-thought play takes a step (a play that only waits fails as 'ran no steps') Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray * test(thought rows): wait for the opened thought's height, not a fixed 500 ms Base's fixed-wait ratchet (#23365) counts agent-pane-step-rows.test.ts at its baseline again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d1f-ca00-7e31-b882-4d3c03033ee2&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…r still (#23302) * test(agent chat): a composer growing at the latest row keeps the scrollbar still Typing new lines in the composer while the transcript follows its latest row grows the room under the last row, so the scroller's content height and offset change on every line and the overlay scrollbar shows and resizes (Leo 2026-10-20). The new browser test samples every painted frame while five lines are typed and removed and requires the scroller's offset, content height and height to stay as they were, with the last row held above the card. It fails on this commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * fix(agent chat): new lines in the composer leave the transcript's scrollbar still While the transcript followed its latest row, the room under the last row was the bottom stack's whole height, so every line the composer card grew (Shift-Return, a wrap) grew the transcript's content and wrote its offset to the new end. The overlay scrollbar showed and resized on every line (Leo 2026-10-20). The card's growth since the view began following now stays out of that room. The rows are drawn up by it with a transform on the thread instead, so the last row keeps its spot above the card while the scroller's offset, content height and height stay put. The rest of the stack (a queue, an ask) makes room as before. When the view stops following, the growth becomes room and the offset moves by it in the same frame, so nothing visible moves. Find, a revealed row and keyboard row focus place rows where they draw, and the mounted range reads the drawn offset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * test(agent chat): report the first frame when the followed scrollbar moves The new test's failure listed only the frames that moved; the first frame and the card's height before typing are what they are compared with, so the report shows them too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * test(agent chat): a grown composer keeps rows in reach and leaves following in place Two browser cases from the review of the composer lift. A transcript a few pixels taller than its view, at its latest row, grows the composer by five lines; scrolling up must bring the first row into view (the lift drew the rows up past what the offset could take back, so the view could not leave the latest row). And a reader who wheels up from a grown composer must see a row move only by the wheel's distance on every painted frame, never by the growth when the view stops following. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * fix(agent chat): the composer lift leaves a short transcript room to scroll From the review of the composer lift. A transcript about a screen tall was drawn up by the whole growth while its offset held only a few pixels, inside the follow deadband: a scroll up never left the latest row, and the first rows stayed out of reach until the draft was sent. The lift is now at most what the offset can take back above the deadband, and the rest of the growth is room. A room held for a sent prompt already covers the card, so that part never lifts, and the lift and the held room round the same way. Writes of the offset by a row's place (Find, a revealed row, keyboard focus of an unmounted row) first stop following and drop the lift, so they land where they mean. The thread opts out of Reduce Motion's short transitions, as the spacer does, so its transform never trails a frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * test(agent chat): a queued prompt sent with a grown composer never drops the reply From the re-review of the composer lift. With five lines typed in the composer while the view follows, the queued prompt's card leaving the stack shrank the lift under the held room, so the transform alone moved every row down by the card, and back up once the prompt arrived. The new case, in Chromium and WebKit, samples every painted frame through the send and requires the reply to move only up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * fix(agent chat): the held room for a queued prompt keeps the composer lift The room a followed view holds for a queued prompt was captured from a content end that had the composer's growth already taken out, and the lift was capped by it, so the queue card leaving shrank the lift and the transform alone moved every row down by the card, then back up as the prompt arrived. The held room is now kept as the end with all the stack's room, the content end is that less the lift, and while it reaches past the rows the lift on screen stays as it is. Converting the lift into offset when the view stops following also updates the transcript's scroll state in the same pass, so the rows mounted for that frame read the new offset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * test(agent chat): the composer lift tests wait for frames, not fixed times The fixed-wait ratchet (#23365) counts the waits these new cases added. They now wait for painted frames, the scroller's offset to rest, the view's resize and the queued card or reply text to show. Calls to the files' shared open() and stream() helpers say why they stay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray * test(agent chat): the stream jitter file declares a 200 s limit With the grown-composer case in both engines the file ran past the runner's 120 s default on a loaded shard (its first run timed out, every case passing). It declares its own limit, as chat-find does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bf1-fdc7-79b0-b869-8ac78b0edd90&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…are one short scale (#23092) * test(newtab): a live resize reads no list row on its frames A live window resize calls the New Tab lists' ResizeObserver every frame, and listMotion read every row's place there (a forced layout per frame). This pins that the rows are read once, after the size holds still. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * fix(newtab): the page lays itself out in CSS while the pane resizes - The field sizes itself (field-sizing: content); the JS fit and its ResizeObserver run only in an engine without field-sizing. - The top padding is one continuous curve in the pane's height, so the composer no longer jumps 55px when a resize crosses 480px tall. - listMotion's ResizeObserver reads no row on a live resize's frames; the places are read once the size holds still for 120ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * feat(motion): one motion duration scale both the app and the webviews read schemas/motion-scale/motion-scale.json holds the scale (quick 80ms, micro 120ms, standard 180ms, large 240ms, ease-out on enter). scripts/cmux-next/gen-motion-scale.py writes its Swift (MotionScale), CSS (--cmux-motion-*) and TypeScript (MOTION_MS) copies; CI and the webviews check run it with --check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * Motion: put Swift and webview durations on the 80/120/180/240 scale Every fade, spring and CSS transition now reads the shared motion scale (schemas/motion-scale/motion-scale.json) instead of its own literal: MotionTunables fades and springs on the Swift side, the host CSS tokens, page literals, Tailwind classes and JS motion constants on the webview side. Springs are critically damped and at rest within the standard step. A static scan (webviews/test/motion-scale.test.ts) fails on any new off-scale duration; MotionScaleTests pins the Swift side. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * test(webviews): New Tab page follows a live window resize Sweeps the New Tab page's iframe from 1100x760 to 480x400 and back, one size per frame, and fails on a composer jump, an inline field height, a missed frame budget or ResizeObserver work per frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * Motion: pane close test follows the faster split spring At response 0.23 s the close is about 0.41 of its travel at 50 ms (it was 0.32 at 0.28 s), so the early-progress window moves to 0.3..0.55. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * New Tab: scroll a capped self-sizing field to the caret in WebKit WebKit sizes the field with field-sizing but leaves a draft past the cap scrolled where it was, so typing at the end hid the caret (composer-draft-wrap "webkit: the New Tab field ... grows to its cap"). On a draft change with the caret at the end, scroll it into view; a resize still runs no script. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * Thread minimap test: per-frame bound fits the 180ms shift glide SHIFT_MS moved from 200 to the scale's standard 180ms, so the fastest frame of the glide is ~40.8px, just over the old 40px bound. 48px still fails a one-frame jump (~150px). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * Motion scale: run its check in ci-web's warn branch; mark the fold hold timer check:motion-scale was in webviews' `bun run check` but not in ci-web's warn-mode loop (test_cmux_next_checks_workflow). foldAnchor's FOLD_SETTLE_MS (from #23093) is a hold timer after a fold, not a transition, so the motion-scale scan allows it by name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * New Tab lists and motion scale: review fixes - listMotion: a row arriving resizes the list itself; the observer then reports the size the effect just read. That no longer marks the places stale, so a second change right after (two tabs closed in a row) still animates. Test: "the list's own size change from a row arriving leaves the next change animated". - motion-scale test: compare MOTION_MS as entries (TS2769 on the readonly literal type in `bun run typecheck`). - RevealedMarkdown: the fresh span outlives the 180ms default-speed fade (standard step), so a reveal's tail no longer pops. - Reduce Motion crossfade in the webviews follows Swift's (quick, 80ms). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * Motion scale test: type the generated-copy comparison as [string, number] pairs Object.entries(MOTION_MS) is typed with the literal step values, so toEqual refused the JSON's plain numbers (TS2769 in the webviews type check). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray * test(newtab): mark the resize tests' fixed waits and frame read for base's new ratchets check:fixed-waits (#23365) and check-frame-reads joined the base's web lint after this branch's tests were written; each wait and the sweep's frame read says why. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15d26-7362-7a82-8d75-ed7d25069378&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…23416) * test(webviews): a fixed-wait check names fixed waits and honors a reasoned marker Fails until scripts/check-fixed-waits.ts exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * webviews: a ratchet refuses new fixed-time waits in tests check:fixed-waits (in `bun run check`) names each test wait for a fixed number of ms: page.waitForTimeout, Bun.sleep, a setTimeout that resolves a promise after a non-zero delay, and calls to a local helper built on one. A fixed wait passes on an idle machine and fails on a loaded runner that wakes it late, or hides the bug it waits out (#23323). A test waits for a condition instead, uses fake timers when the timing is the behavior, or keeps the wait with a `// fixed-wait: <reason>` comment. The 398 waits from before the rule have per-file counts in scripts/fixed-waits-baseline.tsv. A file fails with more unmarked waits than its row, and with fewer until the row is lowered (--update-baseline), so the count only moves down; the sweep PRs lower it. pageSwitch's 16 ms no-requestAnimationFrame fallback keeps its wait with a reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): the fixed-wait check counts helper uses, block-body timers and exported helpers Fails on the first detector: a helper with no delay parameter counted only at its definition, a helper passed as a value (act(settle)) not at all, a timer callback with a block body was missed, and so was an exported helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * webviews: the fixed-wait check counts every use of a sleep helper From review. A wait inside a named function makes it a helper, and each use counts instead of the definition: a call, or the helper passed or returned as a value (act(settle), return { open }), applied outward through helpers built on helpers. So another `await settle()` in a file raises its count. A helper whose delay parameter defaults to 0 waits only when a use passes a delay. An exported helper also counts at its definition, since its callers are in other files. Also caught: timer callbacks with a block body that resolve the promise, window/globalThis.setTimeout and Bun.sleepSync. A marker counts anywhere in the wait's statement, or on a comment-only line above it, so a trailing marker no longer covers the next line. The scan covers every source under test/ (harnesses, latency helpers), not just *.test files, and skips fixtures. A malformed baseline row now fails instead of passing as NaN. The baseline is regenerated: 819 waits in 132 files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): a new wait inside an existing helper must raise the fixed-wait count Review of #23365 found that a second wait or helper call added inside a function that already waits was not counted. These cases fail on the current detector. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * fix(webviews): count every fixed wait and every helper use, inside helpers too A wait counted only through the outer uses of its enclosing helper, so a new wait or helper call inside a helper that already waited changed nothing. Now each unmarked wait site counts, and each use of a helper outside its own body, so any new wait raises the count. Waits in exported helpers count at the site, which replaces the export rule. Export lists, typeof and member declaration names are no longer uses. The baseline is regenerated: 989 waits in 132 files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): U1 waits for conditions, not fixed times Removes the fixed sleeps from the U1 batch's 20 unit-test files. - Hover and dwell delays (session hover card, sidebar preview, minimap turn preview, URL chip grace, settings backdrop and theme dwell, scroll anchor settle, warm New Tab post, prompt delivery deadlines) run on bun:test's fake timers: each test checks the behavior is absent one tick before its delay and present at it, so a slow runner cannot change the result. - Async results (shell reads, sign-in polls, client reconnects and resyncs, pasted image chips, KaTeX and diagram rendering, the code-card handoff, changelog and settings host calls, menu picks) are awaited with bounded `until` loops that yield with setTimeout 0 and fail naming what they waited for. - The settings claim-paint test counts calls and stream events in flight and waits until all are delivered, rather than waiting 300 ms. - The reveal frame helper in transcript-switch-back is left for the shared post-paint frame helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): mark the two 3 s frame-watch windows as fixed waits #23374 merged after the fixed-wait ratchet (#23365) ran CI, and it added a 3 s waitForTimeout in two tests, which puts feat-cmux-next over its baseline. Both waits are observation windows: the test records every painted frame for 3 s after the chat opens and asserts none jumped, so the time is the test. They carry a fixed-wait marker instead of a raised baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): lower the fixed-wait baseline after the base merge Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…23454) * test(webviews): mark the two 3 s frame-watch windows as fixed waits #23374 merged after the fixed-wait ratchet (#23365) ran CI, and it added a 3 s waitForTimeout in two tests, which puts feat-cmux-next over its baseline. Both waits are observation windows: the test records every painted frame for 3 s after the chat opens and asserts none jumped, so the time is the test. They carry a fixed-wait marker instead of a raised baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): B2 waits for conditions, not fixed times Removes the fixed sleeps from the B2 batch's 18 browser-test files. - Daemon round trips: the stand-in acpmux daemons wake a bounded `until` on each message they take, so a test waits for the cancel, prompt, view or offer reply itself, and fails naming what never came. - Absence checks (no cancel, no prompt, no /btw, no armed stop, no second view, no card) wait for a later positive signal first: the page answers a lag notice sent after the input with its refetch, or draws a record sent after it (a failover note or a reply chunk), so anything it would have sent or drawn has arrived before the assertion. Counts of exactly one also wait for that signal. - Stop's 5 s grace and the composer's activation settle run on Playwright's page.clock: the steer and focus-content tests move past them instead of sleeping through them. - The turn footer waits for the footers' opacity to reach its end value; the diff find and working counts read until the state holds (bounded). - Layout settles convert to the shared painted-frame helper (support/paintedFrames.ts): two painted frames after a mount or a font load, the page's finite animations finished first after a fold, a hover, a focus or a resize, and 20 painted frames for the latency probe's frame budget. thin-scrollers waits for data-cmux-scrolling. - reopened-chat-history waits on the daemon's answers (nothing in flight, an older read answered) instead of a 250 ms quiet window. - Two waits keep a reasoned marker: the stream jitter test's chunk cadence is the input under test, and dev-server-stop polls the process table because nothing reports a grandchild's exit. - The stream jitter test's remaining settle and observation windows are left for the frame work (scroll glides are JS-driven, so animation state cannot say they ended). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…es reruns them on the current tip before the merge (#23516) * gh-merge-green: a check path the base changed after the PR's fast gates reruns them on the current tip before the merge A pull request whose CI ran before a check landed on the base could merge without ever passing it: #22967, #22975 and #23094 merged into feat-cmux-next after #23365 added check:fixed-waits and turned it red on the base. Into feat-cmux-next, scripts/ci/merge_check_paths.py reads the base's scripts/ci/check-paths.txt (one list, shared with next-autoland); when a check path changed after the base the PR's cmux-next/fast-gates status names, it dispatches cmux-next-fast-gates.yml for the PR, which merges it into the current tip and runs the cheap gates and the webviews checks, and merges only on a fresh success. Until the list is on the base the gate passes with a note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray * gh-merge-green check-path gate: an answer that is not JSON refuses; the other test fakes have no check-paths.txt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray * gh-merge-green: the check-path gate's wait counts in the summary, notes filter through sed, and --override waives its red From the review of #23516. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray * tests: register test_merge_check_paths.py in the execution registry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…23462) * test(webviews): a fixed-wait check names fixed waits and honors a reasoned marker Fails until scripts/check-fixed-waits.ts exists. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * webviews: a ratchet refuses new fixed-time waits in tests check:fixed-waits (in `bun run check`) names each test wait for a fixed number of ms: page.waitForTimeout, Bun.sleep, a setTimeout that resolves a promise after a non-zero delay, and calls to a local helper built on one. A fixed wait passes on an idle machine and fails on a loaded runner that wakes it late, or hides the bug it waits out (#23323). A test waits for a condition instead, uses fake timers when the timing is the behavior, or keeps the wait with a `// fixed-wait: <reason>` comment. The 398 waits from before the rule have per-file counts in scripts/fixed-waits-baseline.tsv. A file fails with more unmarked waits than its row, and with fewer until the row is lowered (--update-baseline), so the count only moves down; the sweep PRs lower it. pageSwitch's 16 ms no-requestAnimationFrame fallback keeps its wait with a reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): the fixed-wait check counts helper uses, block-body timers and exported helpers Fails on the first detector: a helper with no delay parameter counted only at its definition, a helper passed as a value (act(settle)) not at all, a timer callback with a block body was missed, and so was an exported helper. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * webviews: the fixed-wait check counts every use of a sleep helper From review. A wait inside a named function makes it a helper, and each use counts instead of the definition: a call, or the helper passed or returned as a value (act(settle), return { open }), applied outward through helpers built on helpers. So another `await settle()` in a file raises its count. A helper whose delay parameter defaults to 0 waits only when a use passes a delay. An exported helper also counts at its definition, since its callers are in other files. Also caught: timer callbacks with a block body that resolve the promise, window/globalThis.setTimeout and Bun.sleepSync. A marker counts anywhere in the wait's statement, or on a comment-only line above it, so a trailing marker no longer covers the next line. The scan covers every source under test/ (harnesses, latency helpers), not just *.test files, and skips fixtures. A malformed baseline row now fails instead of passing as NaN. The baseline is regenerated: 819 waits in 132 files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): a new wait inside an existing helper must raise the fixed-wait count Review of #23365 found that a second wait or helper call added inside a function that already waits was not counted. These cases fail on the current detector. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * fix(webviews): count every fixed wait and every helper use, inside helpers too A wait counted only through the outer uses of its enclosing helper, so a new wait or helper call inside a helper that already waited changed nothing. Now each unmarked wait site counts, and each use of a helper outside its own body, so any new wait raises the count. Waits in exported helpers count at the site, which replaces the export rule. Export lists, typeof and member declaration names are no longer uses. The baseline is regenerated: 989 waits in 132 files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * ci(web): run the fixed-wait ratchet in the feat-cmux-next warn branch Under CMUX_NEXT_STYLE_GUARDS=warn the webviews-check step runs bun run check as separate commands, so check:fixed-waits never ran there. It fails the step, like check:browser-lanes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): mark the two 3 s frame-watch windows as fixed waits #23374 merged after the fixed-wait ratchet (#23365) ran CI, and it added a 3 s waitForTimeout in two tests, which puts feat-cmux-next over its baseline. Both waits are observation windows: the test records every painted frame for 3 s after the chat opens and asserts none jumped, so the time is the test. They carry a fixed-wait marker instead of a raised baseline. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): B1 waits for conditions, not fixed times Browser tests in batch B1 wait for the state they then assert instead of sleeping: waitForFunction on focus, results, visibility and editor text (ui-a11y, editor-csp, wheel pass-through, subagent groups), the daemon's own signals (a background request heard or answered, a call heard, the first prompt, the trust check) instead of sleep-polling, the chat list's websocket frame and the stand-in host's pending answers (New Tab first frame and composer), the parked page's claim hook and the dev server's listen line (settings open frames), finished finite animations before measuring an open menu, and the img error event after the inline handler would have run (locale). "Nothing happens" checks get a positive signal first: a console marker after the key for Ctrl-B, the pane's own Escape listener for the question card. The hover check in ui-a11y runs the page's clock past the delay instead of the wall clock. Waits whose delay is the scenario (late daemon and host answers, the moving layout in chat-find) and the no-hint check carry fixed-wait markers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue * test(webviews): B1 frame waits on the shared painted-frame helper The question card's arrival wait and New Tab list motion's "first rows settle" wait now use window.cmuxPaintFrame (test/support/paintedFrames), the latter followed by the rows' finite animations finishing. The inline double requestAnimationFrame waits added in the first commit move to the same helper. Baseline regenerated on the merged base. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15c78-27ca-7140-a6c7-95f0db83b18c&host=huge-blue --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ecks on a dispatch (#23503) * cmux-next fast gates: check paths a merger reads, and the webviews checks on a dispatch A pull request whose CI ran before a check landed on the base could merge without ever passing it: #22967, #22975 and #23094 merged after #23365 added check:fixed-waits and turned it red on the base. scripts/ci/check-paths.txt lists the check scripts, their baselines, webviews' package.json, the fast gates and the workflows; next-autoland and gh-merge-green read it from the base and dispatch the fast gates when one changed after the pull request's fast-gates base. A dispatch now also runs ci-web's fail-mode webviews checks on the pull request merged with the current tip. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray * cmux-next fast gates: the webviews checks fail only on findings the pull request adds over the base A replay of #22975 merged with 01ea78b (the commit that added check:fixed-waits) shows the base already red with two findings and the merge adding a third of its own; charging the base's red to every dispatched pull request would stall them all during a base red. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray * test: each fake commit changes a file Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray * check paths: only the workflows a dispatch reruns, not every workflow A dispatch reruns only the fast gates, so another workflow's change makes nothing stale that it could catch. On 2026-10-21, 144 of 1610 feat-cmux-next commits changed some workflow, about one every ten minutes; with these five, 62 commits changed a check path, about one every 23 minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray * fast gates: review on #23503: every gate input is a check path, the webviews checks run for a catalog or protocol spec change, findings are counted, a dispatch only restores the cache Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray * fast gates: a dispatch only restores the pinned lint tools cache too Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray * check paths: the ratchets' allowlists and vite-plus's lint config (from ci-speed's research) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray * tests: register test_check_paths.py in the execution registry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15bfb-0a99-78c2-b078-ca1fc578ff04&host=cosmic-gray --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Summary
New webview tests can no longer wait a fixed number of milliseconds.
check:fixed-waits, now part ofbun run check(CI'sweb lint (fast)), names each fixed wait in a test file ortest/supporthelper:page.waitForTimeout(…)Bun.sleep(…)setTimeoutthat resolves anew Promiseafter a delay other than 0 (a zero delay only yields to the next macrotask and stays allowed)The scan covers every source under
test/(harnesses, support and latency helpers; fixtures skipped) and the tests undersrc/.A fixed wait passes on an idle machine and fails on a loaded runner that wakes it late, or it hides the bug it waits out. #23323 is an example: a 200 ms wait woke after the card's 250 ms delay. Instead, a test waits for a condition (an event, a DOM state, a painted frame), or uses fake timers when the timing itself is the behavior. A wait that must stay says why in a
// fixed-wait: <reason>comment anywhere in its statement, or on a comment line above it. A marker on a helper covers its uses.The 989 existing waits in 132 files (each wait site counts, and so does each use of a helper that waits, inside other helpers too, so a new wait anywhere raises the count) have per-file counts in
scripts/fixed-waits-baseline.tsv. It works like the type-scale ratchet:bun run check:fixed-waits --update-baseline;--update-baselinerefuses to raise a row.The sweep PRs that follow replace the waits in batches of about 20 files and lower the baseline. This PR changes one wait: pageSwitch's 16 ms fallback for a page with no
requestAnimationFramegets a marker with its reason.How the detector was checked: the AST detector finds every non-zero hit of a broad regex (
waitForTimeout,setTimeout(resolve, N),sleep(N),wait(N)). Every regex hit it skips is asetTimeout(resolve, 0)yield.Test first: the first commit adds
test/check-fixed-waits.test.ts, which fails with the module missing. The second commit adds the script; the test then shows 5 pass. After review, the third commit adds 6 cases that fail on the first detector (helper uses, block-body timers, exported helpers, statement-wide markers), and the fourth commit makes all 11 pass.Observed / Not run
bun test ./test/check-fixed-waits.test.tsred, then green;bun run checkpasses inwebviews/;scripts/web-lint.shandprepush-gate.pypass.Changelog
none
Agent session: huge-blue
🤖 Generated with Claude Code