Repository navigation
Fix desync after Cmd-N: a late Chromium page gets the keys through the focus model; G1 counts cmux pages - #29272
Conversation
…showed must reach the pane's focus model PageWebViewFocusViewChangeTests: a page shown and focused before its Chromium page exists (the first Cmd-N of a launch) must tell its host's focus model when Chromium takes its keys, and when it falls back to WebKit, not grant Chromium focus behind the model. Fails on the base: the page grants focus itself and the host is never told (the action fuzz's W1/W4 desync, 2026-10-31). The test seam: PageChromiumHosting.focusViewChanged (default: no focus model) and FakeChromiumPage.focusAsks.
…ne's focus model; G1 counts cmux pages as hosts W1/W4 (a real desync). A cold New Tab page (the first Cmd-N of a launch) shows before its Chromium page exists, so the focus applier made its WebKit stand-in first responder. When Chromium attached, PageWebView granted the Chromium page focus itself: the stand-in stayed first responder and the applier never recorded the focused page. While the app is active the page window's key change makes the applier re-apply; when it is not (the action fuzz's capture slot, or a person who left the app before the page attached), nothing re-applies: debug.desync kept W1 'appkit content:pane (PageWebView) != model agentPage' and W4 'chromium page nil != model tab' (action fuzz 2026-10-31, v2.2.10-opt 2efedfc3, v2.2.11-opt 78ca62c2), and on return to the app the keys went to the cmux window, not the page (reclaimKeyAfterActivation skips a Chromium agentPage). Now the page asks its Chromium host (PageChromiumHosting.focusViewChanged) when its keyboard view changes (Chromium attached, or a fall back to WebKit); the app's host finds the pane that shows the page and sends contentPresented, so the applier applies the model again: first responder nil, the page recorded and granted, in one key change. A page outside a pane keeps its own grant. G1 (a false finding of the check). ChildPageGeometry.sample listed only browser tabs as hosts, so the page window of every shown cmux page in Chromium was 'page window (618, 268, 892, 692) covers no pane' after each Cmd-N (x11 per round on feat-tip-fixes-v2 f2f3083b, x10 on v2.2.10-opt). Hosts now come from TabContent.childWindowTab, the rule the focus applier and the input observation use. ChildPageGeometry.check had no caller and is removed.
|
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)
Default changesThis pull request changes 1 default at
Signed off on this commit by Merge receipt: merged as Regenerate the snapshot with Shipped in:
|
|
Review (claude-opus-reviewer): FIX at 976b215 Finding 1 (correctness, must fix): the pane lookup takes over pages that the focus applier cannot focus. Result: the docked Home's Channels page loses the keyboard when its Chromium page attaches.
A real case is the docked Home in Channels mode:
Fix: return true, and send Checked, no defect found
|
…el; a nested page keeps its own grant Review of #29272 (FIX at 976b215, claude-opus-reviewer): the pane lookup matched any page inside a pane's content (page.isDescendant(of: content.view)). The docked Home's Channels page sits inside HomeDockView: its attach then skipped its own focus grant, and the applier cannot focus it (childWindowTab is nil for it), so typing went nowhere. The lookup now takes only the pane whose TabContent.childWindowTab is this page's Chromium tab, the exact page the applier gives the keys to; any other page keeps its own grant, as on the base. A fall back to WebKit (no Chromium page) matches no pane and changes nothing, as on the base.
…y with cmux in the background W1/W4 need the app inactive while the New Tab page's Chromium page attaches; debug.window.focus (smoke_make_key) activates the app on feat builds (the feat-tip-fixes-v2 and #29272 runs had app_active true and showed only G1). This variant skips that step and expects debug.focus app_active false, so a pass is about the inactive case.
|
Review (claude-opus-reviewer): SHIP at 5ceaf20 I reviewed the delta 976b215..5ceaf20 (one commit, Finding 1 is fixed
Notes (not blocking)
|
…y what the code does Review at 5ceaf20 (SHIP, note): fallBackToWebKit clears the Chromium session before it called focusViewChanged, so the app's lookup (the pane's own Chromium tab) never matched and the call did nothing. It is removed; a fall back keeps the base behavior. The PageChromiumHosting and PageWebView docs and the test file now describe only the attach case.
… app turns active in the slot Takes back 97c4519. On feat builds (feat-tip-fixes-v2 f2f3083b; #29272 head 5ceaf20, build job 2db9e5949fd59e393aa1bab7) the app is active after one Cmd-N even without smoke_make_key, so the variant's app_active == false never holds there and it can only fail. W1/W4 need an inactive app while the page attaches; on feat the Swift test PageWebViewFocusViewChangeTests (#29272) covers that path. found-desync-cmd-n-page stays.
|
Review (claude-opus-reviewer): SHIP at ad21c67 I reviewed the delta 5ceaf20..ad21c67 and the updated PR body. Delta
PR body
No correctness defect found. |
|
Checks at ad21c67 before ready-to-land: no failed check. |
…esync-channels-type Review of #29274 (FIX at 6e1b67e, comment 6542870799): 1. found-desync-cmd-n-page is first_in_app with a fresh_app step: only the first Cmd-N of a launch starts a cold New Tab page; an earlier journey in the lease could start Chromium and hide W1/W4. 2. It asserts debug.desync check.unsettled is empty: W1-W4 are skipped for an unsettled window. 3. Its why says W1/W4 do not occur on feat builds (the window becomes key in the slot); only G1 makes it red on the feat base. 4. fuzz_daemon_agrees comes last in the expect (it fails on any desync violation too). found-desync-channels-type (coordinator 2026-10-31, the FIX case of #29272's review at 976b215): opens the docked Home's Channels page for the first time in a fresh launch, waits for its Chromium page, then sends real key events through the app's event queue (Cmd-K, then 'zq7'); the page's palette must open and the text must be in its focused field. Needs run.py --whole-host (a key window). Expected red on build 568ad9fb (976b215), green on #29272's later heads and feat.
… typed keys Its first round (build 568ad9fb = #29272 at 976b215, cmux19, whole host, 12:09Z) stopped in the harness: debug.key sequence mode returned 'no event timestamp to start the sequence from' on the capture mini, before any key. The steps before it already showed the defect: Channels in Chromium, its page window visible over the dock, key window ShellWindow, first responder PageWKWebView (the hidden WebKit stand-in). The journey now asserts that state directly after the page paints: the key window is a CefNSWindow and the first responder is not PageWKWebView. The palette click and the typed keys are gone. finally's home.switchView uses focus: true (review note).
…e-existing bug, not of #29272 Rounds through the mini scheduler (spec desync-g1w1w4-journeys, whole host, app active) gave the same state on the feat base f2f3083b, on #29272 at 976b215 (build 568ad9fb) and on #29272's head ad21c67 (build ed24b7b1): Channels in Chromium never gets the keyboard at its first open (key window ShellWindow, first responder the hidden PageWKWebView). The why now says so, with the builds, and no longer claims that the journey tells 976b215 from a fixed build.
|
Note from the #29274 review (#29274 (comment)): on build ed24b7b1 (this head), the app's own monitor recorded one G1 at launch, about 2 s before the journey's first step: report desync-20261031T121749-0001, a page window at (-99590, -99733) 'covers no pane'. That frame is the New Tab spare parked off-screen (NewTabSpareParking puts it 100 000 pt outside the content). This PR does not cause it: the page list of ChildPageGeometry.sample is unchanged, and the host list only grew (childWindowTab still includes every Chromium browser tab), so the set of 'covers no pane' results can only shrink. The live check at 3 s after Cmd-N was empty on this head. I could not read the report file: it stays in the capture slot on cmux20, and the rules forbid connecting to a capture mini. Open item, outside this PR: G1 may need to skip a parked spare's page window. |
…rst open (#29386) * test(home): red: the docked Home's Channels page must be found as the page its tab shows PageWebViewShownInContainerTests: PageWebView.shown(in:) must name the page a container shows now (the docked Home's Channels page inside HomeDockView). It fails here: shown(in:) keeps the old rule (only a content that IS a page), the rule TabContent.childWindowTab has on feat. Because of it, Channels in Chromium never gets the keyboard at its first open (STATUS 1582): journey scripts/journeys/journeys/fuzz/found-desync-channels-type.json, red on feat f2f3083b (cmux19), on #29272 at 976b215 and at ad21c67 (whole host, mini scheduler, 2026-10-31 12:09-12:20Z): key window ShellWindow, first responder the hidden PageWKWebView. * fix(home): the docked Home's Channels page in Chromium gets the keyboard at its first open Channels in Chromium never got the keyboard at its first open (STATUS 1582; red on feat f2f3083b, #29272 at 976b215 and ad21c67, whole host, 2026-10-31). The dock focused the page before its Chromium page existed, so the hidden WebKit stand-in became first responder. At attach the page granted Chromium focus itself and its page window became key, but TabContent.childWindowTab saw only a page tab whose content IS a page. For the docked Home (an internal page whose content is HomeDockView) the focus applier found no Chromium page: it took the key change as unchosen, applied the model again, blurred Chromium pages and reclaimed the keys for the cmux window. Now a container view names the page it shows (PageWebViewContainer; HomeDockView: Channels while it shows), and childWindowTab finds it through PageWebView.shown(in:). The applier then gives the Channels page the keyboard like any page tab in Chromium (one key change, the page recorded), the page window's key change is the pane's own, and G1 counts that page window as its pane's. --------- Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>
…Cmd-N (#29274) * journeys: the action fuzzer as a mode of the smoke-gate driver (ext_fuzz.py, fuzz.py) Brings scripts/journeys (the journeys runner and the dogfood smoke gate, from cp/pa-d-dogfood-smoke-gate at 1222849) to feat-cmux-next, and adds the action fuzz on top of it (cmux-ad 2026-10-31, fuzz-catalog rows a and m): - ext_fuzz.py: step fuzz_run plays a seeded plan of user actions through debug.key, ext_window's drags and the driver's relaunch, at mixed timings, with a model of workspaces/tabs/terminals/panes. Oracles after each burst: create (or a visible refusal), R3a and R3b through ext_smoke's own smoke_r3 / column readings, focus, daemon == app topology, stall > 100 ms in cmux code, crash, leak after close-all. A failure is shrunk in the same app with ddmin. Speed marks per create: shown_ms, typeable_ms. - fuzz.py: rounds through run.py (exclusive, pool perf-lead, never cmux17), --base A/B on the same mini (ABBA), confirms each shrunk repro in a fresh lease and writes it as a regression journey; verdict CLEAN/RED/NOT JUDGED/DIAGNOSTIC, .clean file only for a judged clean run. - Sets: layout (row m: New Column, Cmd-D, Split Down, Ctrl-`, close pane, divider drag, window resize, relaunch) and full. - journeys/fuzz/: the fuzz journey and Lawrence's reported bugs as fixed sequences (lawrence-*.json). - test_fuzz.py (plan, ddmin, speed table, verdicts with a stand-in run.py) and test_fuzz_sim.py (the oracles against a simulated app with planted bugs): python only, no fleet. Tests: python3 scripts/journeys/test_fuzz.py (16 OK), test_fuzz_sim.py (8 OK), test_smoke_gate.py (OK), run.py --check (59 valid). * journeys fuzz: the corpus plays as one round per set (a close-all between Lawrence's reports) * journeys fuzz: review of 0224106 (BLOCK 1-3) and the first fleet trial - A round whose journey failed outside the fuzz verdict (smoke_ready, the final expect's other checks, a harness error) is RED, never skipped. An action whose input path fails is recorded and judged by the oracles (crash first) instead of ending the step. - predict: a workspace create resets the pane count (delta counts the shown workspace only). - paste: a comment line plus Return, so the next probe's line is clean. - desync, stall and focus are recorded findings (once per signature, with the seed and the actions before), not stops: the first trial on v2.2.11-opt and v2.2.10-opt stopped at the first Cmd-N on desync W1/W4/G1 and a 190 ms CefInitialize stall on both builds. A finding only on the build (not on the base) is RED; one on both is a base finding. - R3b read through settle_check; launch stalls after a relaunch skipped; shrink replays add no speed samples or findings. - Verdict: CLEAN needs --base, --minutes > 0, the full fuzz time and no replay; cmux20 (the quiet bench host) out of the default hosts. Tests: test_fuzz.py 19 OK, test_fuzz_sim.py 9 OK, 80 sim seeds clean. * journeys fuzz: R3b final scope (cmux-ad 08:06Z) and a fresh start for every run and shrink try - R3b: New Column (Ctrl-Cmd-D) keeps every old column's exact width; Cmd-D and Split Down split inside a column and no OTHER column changes width. Cmd-D is no longer a column action. New Column always starts its burst, so a red names the action that changed the column. - The layout set starts each run and each shrink try with every workspace closed and one fresh Ctrl-` workspace: the first fleet runs' shrink tries started in a workspace an earlier try had split, so ddmin could not shrink. - Corpus lawrence-column-widths: New Column twice, Cmd-D, Split Down, a New Column pair. - Sim: inside splits; planted 'a split resizes another column' is r3b, a split alone is not. Tests: test_fuzz.py 19 OK, test_fuzz_sim.py 12 OK. * journeys fuzz: review of 12857a6 (BLOCK: window ids in finding signatures); the first red repro - signature() drops UUIDs, window ids and long hex: one focus or desync finding on the build and the base is one signature (no false RED). - paste only into a focused terminal (Return would submit a New Tab field). - The corpus round runs in the chosen set (the layout corpus gets the fresh start). - journeys/fuzz/found-r3b-new-column-narrows.json (P0, red on feat-tip-fixes-v2 f2f3083bac80ab71101d03ac): Cmd-N, Ctrl-`, New Column; the old column went 872 -> 291 pt. Owner a03ef38e4dc40283d. Tests: test_fuzz.py 20 OK, test_fuzz_sim.py 12 OK, run.py --check 60. * journeys fuzz: red repro found-r3a-split-fit-grid-stale (v2.2.10-opt: Cmd-N, Ctrl-`, Cmd-D; fit_grid 110x40 vs grid 36x40) * journeys fuzz: size trace (daemon size, app grid/fit/drawn, SIGWINCH) per terminal, on in replays; Cmd-N first needs no Ctrl-` set-up * journeys: a capture lease is held only while journey steps run (cmux-ad 09:05Z) - run.py: SIGTERM raises Terminated, so run_lease's finally releases the lease; an acquire stopped by a signal is ended (capture-host unreserves). - run.py: the build comes from a per-mini cache outside the slot (~/.cache/cmux-journeys/builds/<job>/, newest 3), filled atomically by the first lease of that job; later rounds download nothing. - fuzz.py: ddmin runs outside the round, one short replay lease per try (--shrink-tries 10); the in-app shrink is off by default. fuzz.py passes its own SIGTERM to run.py as TERM (never KILL) and waits. - test_run_release.py: release after an error inside the round, after SIGTERM while driving, the acquire ended after SIGTERM while acquiring, and fuzz.py's SIGTERM reaching run.py as TERM. Tests: test_run_release.py 4 OK, test_fuzz.py 20 OK, test_fuzz_sim.py 12 OK, test_smoke_gate.py OK, run.py --check 61. * journeys fuzz: cmux21 (the tip watcher's smoke candidates) out of the default fuzz hosts * journeys run.py: a second stop never kills the lease release (review of 22115f3, BLOCK) - SIGINT and SIGTERM are blocked from before the acquire until the lease is inside run_lease's try, and again for the whole clean-up (hog stop, release); a stop that came meanwhile is raised after the release. While acquiring, a pending stop ends the acquire (its trap unreserves); the acquire child runs with the signals unblocked. - The release itself cannot raise past the finally (OSError, timeout). - Build cache: the job id must be a plain id; pruning by use (the dir is touched on a hit); stale .tmp folders over 60 min removed. - test_run_release.py DoubleStop: TERM during the driver, then TERM and INT during a 3 s release: release-done must be logged. Red on 22115f3, green here. Tests: test_run_release.py 5 OK, test_fuzz.py 20 OK, test_smoke_gate.py OK. * journeys fuzz: normal mode is --whole-host HOST (the app active, its window key); without a key window it is 'not run: known gap' Normal mode types at once after each create and judges focus against AppKit's first responder, which needs a key window: fuzz.py refuses it without --whole-host (use --diagnostic in a slot), passes --whole-host to run.py (whole-mini exclusive lease, app launched active), and ext_fuzz.Fuzz raises NotRun when smoke_make_key did not make the window key, so a slot's limit is never a product red. Tests: test_fuzz.py 22 OK, test_fuzz_sim.py 13 OK, test_run_release.py 5 OK. * journeys run.py: a lease the acquire printed as the stop arrived is released (cmux19 09:36Z leak); fuzz default hosts: cmux19 only until each Cua grant - On a stop while acquiring, run.py reads what the ended acquire printed and releases each lease in it (review note on 22115f3; it happened on cmux19 at 09:36Z: released by hand after 61 s). - test_run_release.py LateLease: red on 4c17e14, green here. - DEFAULT_HOSTS = cmux19: cmux18/22/23/24 wait for their 'CUA GRANTED' line (lead 09:3xZ), cmux20 is row m only, cmux21 the smoke candidates. * journeys(fuzz): found-desync-cmd-n-page, the red journey of the fuzz desync findings G1, W1 and W4 after Cmd-N One Cmd-N in a fresh launch, a 3 s settle, then debug.desync check must list no G1, W1 or W4 (STATUS 1002, 1022). Red on v2.2.11-opt (job 78ca62c225bb0943594e3cd2, cmux19, diagnostic mode): W1 'appkit content:pane (PageWebView) != model agentPage', W4 'chromium page nil != model tab' and G1 'page window (618, 269, 892, 692) covers no pane', still there 13 s after the key. debug.focus: app_active false, first responder PageWebView, model agentPage. The fix is #29272 (feat-cmux-next). * journeys(fuzz): found-desync-cmd-n-page-inactive, the same red journey with cmux in the background W1/W4 need the app inactive while the New Tab page's Chromium page attaches; debug.window.focus (smoke_make_key) activates the app on feat builds (the feat-tip-fixes-v2 and #29272 runs had app_active true and showed only G1). This variant skips that step and expects debug.focus app_active false, so a pass is about the inactive case. * journeys(fuzz): drop found-desync-cmd-n-page-inactive: a feat build's app turns active in the slot Takes back 97c4519. On feat builds (feat-tip-fixes-v2 f2f3083b; #29272 head 5ceaf20, build job 2db9e5949fd59e393aa1bab7) the app is active after one Cmd-N even without smoke_make_key, so the variant's app_active == false never holds there and it can only fail. W1/W4 need an inactive app while the page attaches; on feat the Swift test PageWebViewFocusViewChangeTests (#29272) covers that path. found-desync-cmd-n-page stays. * journeys(fuzz): review fixes for found-desync-cmd-n-page; add found-desync-channels-type Review of #29274 (FIX at 6e1b67e, comment 6542870799): 1. found-desync-cmd-n-page is first_in_app with a fresh_app step: only the first Cmd-N of a launch starts a cold New Tab page; an earlier journey in the lease could start Chromium and hide W1/W4. 2. It asserts debug.desync check.unsettled is empty: W1-W4 are skipped for an unsettled window. 3. Its why says W1/W4 do not occur on feat builds (the window becomes key in the slot); only G1 makes it red on the feat base. 4. fuzz_daemon_agrees comes last in the expect (it fails on any desync violation too). found-desync-channels-type (coordinator 2026-10-31, the FIX case of #29272's review at 976b215): opens the docked Home's Channels page for the first time in a fresh launch, waits for its Chromium page, then sends real key events through the app's event queue (Cmd-K, then 'zq7'); the page's palette must open and the text must be in its focused field. Needs run.py --whole-host (a key window). Expected red on build 568ad9fb (976b215), green on #29272's later heads and feat. * journeys(fuzz): found-desync-channels-type types plain letters into a field the page focused itself Review of #29274 (FIX at c5cba01, comment 6542986147): 1. Cmd-K is an app shortcut over a Chromium page (agentPane.searchChats) and never reached the page. Now a click in the page's own script (debug.page click .lg-search: no AppKit event, no key-window change) opens the page's palette with its field focused in the DOM (checked before any key), and plain letters 'probe' go through the app's event queue to whatever window is key. 2. smoke_make_key added: fuzz_run (the closing daemon health check) needs it. 3. frame_text reads the typed word from the window; why says the closing daemon check is the harness's required evidence, not the claim. 4. finally switches the dock back to Messages and unsets home.channelsEnabled. * journeys(fuzz): found-desync-channels-type reads the focus state, not typed keys Its first round (build 568ad9fb = #29272 at 976b215, cmux19, whole host, 12:09Z) stopped in the harness: debug.key sequence mode returned 'no event timestamp to start the sequence from' on the capture mini, before any key. The steps before it already showed the defect: Channels in Chromium, its page window visible over the dock, key window ShellWindow, first responder PageWKWebView (the hidden WebKit stand-in). The journey now asserts that state directly after the page paints: the key window is a CefNSWindow and the first responder is not PageWKWebView. The palette click and the typed keys are gone. finally's home.switchView uses focus: true (review note). * journeys(fuzz): found-desync-channels-type is the red journey of a pre-existing bug, not of #29272 Rounds through the mini scheduler (spec desync-g1w1w4-journeys, whole host, app active) gave the same state on the feat base f2f3083b, on #29272 at 976b215 (build 568ad9fb) and on #29272's head ad21c67 (build ed24b7b1): Channels in Chromium never gets the keyboard at its first open (key window ShellWindow, first responder the hidden PageWKWebView). The why now says so, with the builds, and no longer claims that the journey tells 976b215 from a fixed build. * journeys(fuzz): move found-desync-channels-type out of this PR Coordinator decision: the Channels journey is the red test of a pre-existing feat bug (STATUS 1582: the Home dock Channels page in Chromium never gets the keyboard at its first open), so it goes with that bug's fix PR. This PR keeps only found-desync-cmd-n-page. * journeys(fuzz): found-desync-cmd-n-page waits for a settled window before it checks G1, W1, W4 One expect step held all conditions, and step_expect locks in a positive condition (a `lacks` too) the first time it holds. While the window was still unsettled the app skips W1-W4, so "no W1" and "no W4" passed at the first poll and a W1 that came one poll later did not make the journey red (review of 416219a). Now one step waits for check.unsettled to be empty, and the next one checks no G1, W1, W4, the frame and the daemon. --------- Co-authored-by: cmux-bot[bot] <348531757+cmux-bot[bot]@users.noreply.github.com>
Summary
The action fuzzer (#29071) recorded three
debug.desyncfindings after Cmd-N (STATUS 1002, 1022). This PR classifies each one and fixes it at its cause.W1 and W4: a real desync. At the first Cmd-N of a launch, no New Tab spare exists and Chromium is not started, so the New Tab page (an
agentPage) shows before its Chromium page exists. The focus applier makes the page's WebKit stand-in first responder. When the Chromium page attaches,PageWebView.attachChromiumgranted Chromium focus by itself, behind the focus model. The stand-in stayed first responder, and the applier never recorded the focused page. When the app is active, the page window's key change (childWindowDidBecomeKey) makes the applier apply the model again. When the app is not active (the fuzzer's capture slot, or a person who switched away before the page attached), nothing applies it again. The world check then keepsW1 appkit content:pane_* (PageWebView) != model agentPageandW4 chromium page nil != model tab_*. The window becoming key again heals it.Fix: when a page's Chromium page attaches, the page asks its Chromium host (
PageChromiumHosting.focusViewChanged, default: no focus model). The app's host finds the pane whose own Chromium page (TabContent.childWindowTab) this is and sendscontentPresented. The applier then applies the model as it does for any newly presented content: no responder in the cmux window, the page recorded and granted, in one key change. Any other page (the docked Home's Channels page, a page that no pane shows, a parked spare) keeps its own grant, as on the base. A fall back to WebKit is unchanged.G1: a false finding of the check.
ChildPageGeometry.samplecounted only browser tabs as hosts. The page window of every shown cmux page in Chromium (the New Tab page, an agent chat) was then reported aspage window (618, 268, 892, 692) covers no pane. Hosts now come fromTabContent.childWindowTab, the rule that the focus applier and the input observation already use. The same sample drivesWindowOverlayLayer.pageWindowDidChangeFrame, which forced a page update on each frame change of such a page window.ChildPageGeometry.checkhad no caller and is removed.Testing
Red first:
PageWebViewFocusViewChangeTests(CmuxNextPagesTests), added at a346e23.page.focusAsks → [true]).found-desync-cmd-n-page(journeys(fuzz): red journey for the desync findings G1, W1, W4 after Cmd-N #29274): one Cmd-N, a 3 s settle, thendebug.desync checkmust list no G1, W1 or W4.Observed / Not run
Observed (journey, cmux19, exclusive capture lease, diagnostic mode):
debug.focus: first responder PageWebView, model agentPage.app_active == falseprecondition. Do not count it.Not run, or read only:
Changelog
none
Proof
No capture: there is no visible change. The fix changes which view holds the keyboard after a cold New Tab page attaches while cmux is in the background, and what a debug-only check reports.