Skip to content

fix(webui): keep the live-turn mark honest, and let the browser-driven gates run - #242

Merged
modacker merged 11 commits into
webuifrom
fix/main-red-fork-title-and-browser-gate
Oct 23, 2026
Merged

modacker merged 11 commits into
webuifrom
fix/main-red-fork-title-and-browser-gate

Conversation

@modacker

@modacker modacker commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Change

The webui branch was red in three places, and the reds were hiding each other: the first gate that failed stopped the ones after it, so a second and a third failure had never run. This PR fixes them in order.

The client

  • The composer lost a typed fork title. Opening the fork dialog always prefilled the suggested title, so a title the user had typed was replaced by the suggestion on the next open. The dialog now remembers whether the title was edited and only prefills when it was not.
  • A catch-up attach marked the session as having a live turn. A session.start recheck resolves against the runtime's active turn. When that turn had already finished by the time the probe answered, the client attaches to pick up the frames it produced — an attach with no turn in it, because there is no turn left to name. That attach raised the session's sending flag, and sending is what the submission admission reads as "a turn is live": the next message was queued instead of sent, and the composer's send button became the stop button, which swallowed the click that was meant to send it. An attach that cannot name the turn it follows no longer marks the session live, and the probe's answer has to name the turn for the client to follow it at all.

The gates

  • The CI runner had no browser. The test:webui gate runs before the gate that installs playwright's chromium, so a browser-driving case in that group had nothing to launch. The browser install now runs ahead of it.
  • The browser-driven cases starved each other. Two suites in that group drive a real browser, and on a runner with three cores the two browsers took the machine from each other until a browser command exceeded the provider's deadline and the case failed for a reason that had nothing to do with the code under test. The group now runs its files one at a time, the way the Windows profile already does: the files are serialized rather than the deadlines relaxed.

The cases that the newer changes had moved under

eb6cd3e5 redesigned the workspace's new tab page and 4d06e817 added the transcript case that drives the browser panel. Both landed while the gate that would have said so was not running.

  • The W0 structural case read the expanded panel's position through an exact selector match, which no longer finds the rule now that the expanded and covering panels are declared together; it now reads the panel through the cascade lookup it already has, and lists the panel's new keyframes.
  • The launcher's content is centred in the panel body now that its padding is symmetric: the brand and the tiles are the first and last children of a centred column, so the midpoint between them is the launcher's own vertical centre. The case that read "strictly above the centre" now reads the centring the new tab page has.
  • The embedded browser panel's visual baseline is the old new tab page. It is regenerated on the CI runner's own Chromium, and two consecutive runs produce byte-identical files.

Validation

  • pnpm --filter @mavis/webui typecheck:client, typecheck:test — clean.
  • packages/webui/test/unit/webui-w0-css-structure.test.ts — 29 passed.
  • packages/webui/test/integration/agent-transcript.test.ts — 10 consecutive green runs locally (Chromium 153, the same build CI uses). Before the live-turn fix the same case failed roughly half the time.
  • packages/webui/test/browser/browser-workspace.spec.mjs — 10 passed.
  • The whole webui group — 212 passed, 4 skipped.
  • The platform matrix — see the checks on this PR.

Publication and contribution checks

  • I have permission to contribute these changes under the existing licenses applicable to the changed files/packages; imported material and its provenance are identified and existing notices are preserved.
  • No credentials, account data, real user content, internal source history or private review material is included.
  • Added/removed source files were reviewed before regenerating release/public-source.json; new tests are declared in test/vitest-suites.json where applicable.
  • Shared English/Chinese documentation and capability/verification records are updated where applicable. Mock/offline results are not described as live-service acceptance.

Maintainer handoff

Publication scope or license changes (if any): none.

Shared-source port: not needed. Record public PR references only; keep private links and review material out of this PR.

probe added 8 commits October 23, 2026 15:59
…e gate that needs it

Two failures keep the webui branch red, and both live in code the branch
carries today.

The fork dialog seeds its title field from the fork options. Those options
arrive after the dialog is already open, so the seed is a late reply: a name
the person typed into the dialog could be overwritten by the suggested title
that landed afterwards, and the fork was then created with the session's own
title instead of the name that was asked for. The dialog now remembers
whether the name has been edited, and only seeds the field when it has not.

The WebUI integration suites drive a real Chromium - the loopback host test
opens the built client in one and submits through it. The browser used to
arrive with the `test:webui-browser` gate, which runs after `test:webui`, so
a suite inside `test:webui` that needs a browser depended on a later gate
having run first. The case that exercises the loopback host found it: the
browser was simply not there. A dedicated gate now installs it before
`test:webui` runs.

The install stays free of `--with-deps` on purpose. Playwright's dependency
installer has a Windows branch that shells out to PowerShell, and this gate
runs on every platform. The Linux browser gate keeps its own `--with-deps`
install, because that is what the visual baselines need - the CJK fonts
among them.
The fork case read the reply frame once, right after the request had been
observed. The reply travels on its own frame, so it can land later than the
request, and the read then found nothing. Wait for the forked session's id
itself, which is what the assertion is about.

No expectation changed: the id still has to be a string.
The transcript-and-composer baseline was regenerated on a workstation, so it
carried that machine's glyphs. On the Linux runner that runs the browser
gate the same labels are drawn with the runner's CJK font, and the image no
longer matched: 7415 pixels, a ratio of 0.01 - the size of a font
difference, not of a layout change.

The baseline is regenerated here on the CI runner, in the same Linux
Chromium the browser gate compares against, which is the only environment
that can produce a matching image. Two runs of the regeneration are
compared byte for byte before the image is taken, and they are identical.

The other nine baselines in this file are unchanged.
The case waits for the model server to see the request that starts a
cancelable local response. The wait is for that event; the ceiling only
decides how long the case may look for it.

Twenty seconds is enough on Linux and not on macOS, whose whole verification
run is about 1.5x the Linux one - 197.8s against 134.3s, measured on the
same gate set. This step is also the first in the case that only runs once
the earlier assertions hold, so on macOS it is the first one to be reached,
and it is the first to hit the ceiling.

The ceiling for this wait goes to 60 seconds, the same figure the isolated
Chromium suite uses for its own slower steps. No expectation changed, and
the other two waits keep their 20 seconds.
A `session.start` recheck resolves against the runtime's active turn. When
that turn had already finished by the time the probe answered, the client
attaches to pick up the frames it produced — an attach with no turn in it,
because there is no turn left to name.

That attach raised the session's `sending` flag, and `sending` is what the
submission admission reads as "a turn is live". So the next message was
queued instead of sent, and the composer's send button became the stop
button, which swallowed the click that was meant to send it. An attach
that cannot name the turn it follows therefore never marks the session
live, and never clears a mark it did not raise.

The probe's answer also has to name the turn for the client to follow it
at all: an answer without a turn id cannot be resumed, and a lease opened
for it would hold the next `session.start` off instead of letting this
client follow it.
The expanded panel is declared together with the covering one, so a lookup
that matched the selector string exactly found only the media-query rule
that carries the panel's width and read no position at all. The cascade
lookup is the one that reads a grouped selector, so the panel's position
and inset are read through it.

The panel now covers its own container — which the container already pins
to the viewport — rather than the viewport itself, so the position the
rule declares is `absolute`.

The workspace panel's reveal is three more declared keyframes, which the
"every keyframe animation the shell references" case now lists.
@modacker modacker changed the title fix(webui): keep a typed fork title and install the browser before the gate that needs it fix(webui): keep the live-turn mark honest, and install the browser before the gate that needs it Oct 23, 2026
probe added 3 commits October 23, 2026 18:57
The launcher's padding became symmetric, so its content now sits exactly on
the panel body's centre line: the brand and the tiles are the first and last
children of a centred column, so the midpoint between them is the launcher's
own vertical centre. The assertion said "strictly above the centre", which
the symmetric padding made unreachable, and it now fails with the two sides
exactly equal. Assert the centring the new tab page actually has.
The new tab page the workspace launcher draws is not the one the baseline
was taken from: the brand is the MiniMax wordmark instead of the app icon,
and the address box is the wider rounded field of the redesign. The baseline
was never regenerated because the gate that compares it had not run since
the redesign landed. It is regenerated on the CI runner's own Chromium, and
two consecutive runs produce byte-identical files.
Two files in the WebUI group drive a real browser, and they were running at
the same time as each other and as the rest of the group. On a CI runner
that is a browser per file competing for the same cores: the case that
drives the browser panel needs about half again the time it gets, and it
runs past its own deadline — a failure that says nothing about the code
under test. The same thing already happens on Windows, where the whole
suite is serialized for I/O contention.

Serialize the group's files instead of relaxing any deadline: the browser
gets the machine to itself, and every deadline stays the one its case was
written against.
@modacker modacker changed the title fix(webui): keep the live-turn mark honest, and install the browser before the gate that needs it fix(webui): keep the live-turn mark honest, and let the browser-driven gates run Oct 23, 2026
@modacker
modacker merged commit 571a93d into webui Oct 23, 2026
19 checks passed
@modacker
modacker deleted the fix/main-red-fork-title-and-browser-gate branch October 23, 2026 11:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant