Repository navigation
Agent pane: a chat's last frame stays until its live page is ready, instead of going dark - #24262
Conversation
Leo, 2026-10-22, on a chat at launch: it "flickers the initial thing/paints and renders it... and then it just goes to black screen for a while". The pane draws the chat's last frame at launch (AgentPaneLaunchImages) so a relaunch is never an empty pane, and turns its own loading state off under it. That frame was taken back after paneLaunchImageLimit whether or not the live page had drawn, and clearing it turns the loading state back on: a flat page background with a busy mark. The page, meanwhile, reports its first frame only once it shows what it is, which waits on acpmux reading the chat (CHAT_DRAW_HOLD_MS, ten seconds). On a busy daemon the two miss each other by seconds, and the pane spends them dark between a chat it had drawn and the same chat live. The limit now asks before it takes the frame: a frame standing in for a page that has not drawn stays. Nothing waits for ever, because the pane already gives up on a page that never reports a first frame and shows it as it is (AgentPaneLoadingView), and that reveal clears the frame — every path ends on the page rather than on an empty pane. A frame held over a page that has already drawn (the New Tab cover) keeps the old behaviour, and LaunchImageHold's backstop is unchanged, so deferred launch work still runs. Leo-ask: ask-665f9ad8ba Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cb8-bbf0-7010-8d04-f9f2c0cb55df&host=big-red
Bugbot needs on-demand usage enabledBugbot uses usage-based billing for this team and requires on-demand usage to be enabled. A team admin can enable on-demand usage in the Cursor dashboard. |
|
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)
red: cmux-next checks (god files, concurrency, crash safety, l10n); no finished base run on 8711a8c to compare; do not merge until green; see lane-rules |
|
Owned by nx-chat on big-red · took over from session 01a15cb8 |
|
Bead |
|
Found 8 test failures on Blacksmith runners: Failures
|
|
|
…p the test leaking the hold Three findings from the independent review of this PR, all of them about the change itself rather than follow-up work, so they land on it. `paneLaunchImageLimit`'s doc comment said it was "how long a launch image may stay when its page never reports a first frame" — exactly the case this PR stops it from bounding. What bounds that case is `AgentPaneLoadingView`'s give-up, two unpainted checks at `paintCheckInterval` 4 s, so about 8 s; `LaunchImageHold`'s comment had the same stale "or at its limit". Both now name the real bound, and record that deferred launch work is held by the hold's own backstop (the limit plus a second), which no image's deadline feeds. The deadline's comment no longer reads as if the dark gap were closed. The give-up at ~8 s is the earlier of the two budgets against the page's ~10 s `CHAT_DRAW_HOLD_MS` after its handshake, so a daemon slow enough to miss it reveals a page that is itself still blank. This PR narrows Leo's gap from about seven seconds to about two; closing it means moving one of the two budgets, which is a change to the page's contract and not this one. `LaunchWindow.tearDown` now clears the frame. The first new test deliberately ends with its frame up, and `PaneContentView`'s deinit does not end the hold (by design: "a pane that went away with its image up never ends it"), so `LaunchImageHold.shared` kept a count for the rest of the test process. Nothing in the suite awaits it today, which is why it was invisible; the next test that did would have taken the 4 s backstop instead of returning at once. Fixing it in the fixture rather than per test means a later test cannot forget it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cb8-bbf0-7010-8d04-f9f2c0cb55df&host=big-red
…ecome instantiable structs (#24330) `cmux-next checks` is red on every feat-cmux-next PR. Its namespace-type rule rejects `AgentTurnRing` (AttentionHighlightLook.swift) and `AgentPaneNeedsYouSetting`, both all-static public enums, and a lint red also skips the checks that run after it in the same job (#22197), so it costs more than the two lines it names. `scripts/lint-ios-package-conventions.sh --namespace-fix`, the repo's own fixer, makes each an instantiable struct with a public init. Every reference is a static member (`AgentTurnRing.tunable`, `AgentPaneNeedsYouSetting.enabledPath` and `.fallback`); nothing extends or conforms either type, and neither is used as a metatype, generic argument or switch subject, so no call site changes and no behavior does. Base red, not this lane's: the same five checks fail with a byte-identical failure set on a markdown-only PR (#24269) and a Swift PR (#24262), and both violations are present at base tip 50a7cb4. Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cb8-bbf0-7010-8d04-f9f2c0cb55df&host=big-red Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
|
Route change on the red leg, so this PR's description does not stay wrong: it says The fleet step never got a worker. Dispatched 06:58 UTC as a #24357 is this branch's head with exactly one line removed: - guard launchImageMayClear?() ?? true else { return }One deletion, one file, the two new tests untouched, so a red there is attributable to that line
|
…tely blank
Leo 2026-10-22: "the initial load is kinda slow it like flickers the initial
thing/paints and renders it... and then it just goes to black screen for a
while". The second half of that is two budgets that do not agree.
The pane's loading state gives up on a page that has not reported a first paint
after two checks in sight (about 8 s) and shows the page as it is. That exists
for a page that cannot report at all: WebKit holds the animation frames of a
window it thinks is not visible, so `pane.painted` may never come (cx-txnr).
But the agent page also holds its own first paint back for up to
`CHAT_DRAW_HOLD_MS`, 10 s after its handshake, while it waits for the chat's
rows -- a hold that was itself raised to 10 s because a shorter one showed a
busy daemon's chat as an empty page ("I LOAD IN AND IT'S A BLACK SCREEN",
2026-10-21). So the host revealed, at 8 s, a page that intended to stay blank
until 10 s: the black screen.
A page that answered the handshake is not the stuck case. Its bridge is up and
it will report by its own deadline whether the chat arrived or not, so it now
gets `aliveChecks` checks instead of `silentChecks`, counted from the handshake
-- the first check that sees it answer starts the count over, because a slow
daemon can answer at any point inside the silent bound, and a bound measured
from the pane would still fire early for a late handshake. A page that says
nothing at all keeps today's bound exactly.
The two numbers have to stay ordered, so `theAliveBoundClearsThePagesOwnBudget`
reads `CHAT_DRAW_HOLD_MS` out of App.tsx rather than restating it: raising
either side alone fails that test instead of bringing the gap back quietly.
This is the half of the symptom that #24262 narrows but cannot close -- that one
keeps the pane's own last frame from being taken early, this one keeps the pane
from revealing a blank page -- and the cost is that a page which handshakes and
then truly hangs holds a spinner longer than before, which is the right way
round: a spinner says loading, a black pane says broken.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?harness=claude&session=01a15cb8-bbf0-7010-8d04-f9f2c0cb55df&host=big-red
|
The other budget now has a PR: #24384 moves the host's give-up so the pane stops revealing a page They are independent: different files, no shared symbol, and neither depends on the other landing |
|
Orphaned: no activity for 13 h, and no lane holds a lease on it. Adopted by the orphan sweep: merged |
|
|
|
Review (codex gpt-6-astra xhigh, next-review): SHIP at 5caf949 No blocking findings. |
|
|
|
|
|
Shipped in:
|
![Fix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/fix-with-codesmith-light-3x.png)
Leo, 2026-10-27: “There's still that black screen and what not in the agent chat area. Plus we have this flicker render when it starts trying to render a bunch of stuff again.”
The saved transcript frame was removed after three seconds even when the live chat page had not painted, so the pane briefly replaced a visible chat with its empty loading state.
Keep that frame until its page is revealed. A cover over a page that has already painted still expires at the normal deadline. Delayed reveals carry their frame's identity, so an old tab cannot clear the next tab's cover; closing, moving or parking a page clears its held frame and predicate. The native Loading readiness budget is a separate fix (#24384 successor), and this patch does not claim to close that later reveal gap by itself.
Timer stop and cleanup: the launch-image deadline is one-shot, cancelled by
clearLaunchImage. The saved frame ends on live-page reveal or content detach/tab change.clearLaunchImagealso drops the predicate and callback and endsLaunchImageHold; deferred launch work retains its independent four-second backstop.Reproduce and repair
Focused command for both legs:
322c1f5c715dc58917cf67102845434bf8515dearemoves only the deadline guard from the retained Agent pane: a chat's last frame stays until its live page is ready, instead of going dark #24262 head. Fleet stepcd3f9a67d414ef7619ff0d3efailed onaws-m4pro-2with exactly the expectedlaunchImageView == nilfailure intheLastFrameWaitsForThePageItStandsInFor; the already-painted expiry canary and two other tests passed. The screen-dependent fade test skipped.39ba84ea217428a0dd1cb12b63005ea06d2c212d; exact-head native suiteb6bc8838dbf524477dbfa7a0timed out at 2400 seconds before the suite began (after restoring web bundles). DEV app buildccaf9a31213b06b773d16c2ccompleted successfully; artifact SHA-256c4b51ff0e4aa356bb592d58140f8e45da34257a9cc0d7805c5e962080e677d39.Observed / Not run
Observed: scoped Swift parse and selected static preflight pass;
prepush-gate.pyreports no new violations. Independent Codex gpt-6-astra xhigh review is SHIP at39ba84ea217428a0dd1cb12b63005ea06d2c212dafter verifying the stale-reveal and detach repairs. Source and lifecycle review identify the three-second withdrawal and the stale reveal/displacement paths.Final native focused recovery step
34b1c81373ccfc4e8cabeebcpassed at the same exact head onaws-m4pro-2, exit 0:LaunchFirstFrameRevealTestspassed in 10 seconds, with one existing no-screen fade test skipped. The original suite attempt timed out before tests. Two isolated DEV capture attempts produced a real seeded mock-pane snapshot and 308 first-launch frames, but the relaunch recorder only captured the disposable launch window; the corrected canonical-workspace attempt stopped yielding an agent pane and subsequent snapshot RPC timed out. Neither attempt is a relaunch retention pass. Published nightly verification remains not run.Changelog
Fixed - A relaunched chat keeps its last transcript frame until its live page is ready; closed or moved pages release their own covers.
The saved-frame retention fix is verified by the native red-to-green regression, exact-head DEV build and independent review. A real DEV WebKit relaunch restored the same chat in 303 composited frames with zero black frames; this is normal-load WebKit evidence, not a controlled before/after or Chromium proof. The full Chromium relaunch no-black capture follows once capture collects first-second launch frames; fanout-infra owns that collector work and sanitizing the fake-chat fixture. The separate post-handshake loading deadline is fixed in #26161.
Review (Codex gpt-6-astra xhigh): SHIP at 39ba84e
Agent session: agent session