Review found two factual errors in the prose this change adds.
plugin-install-cancel: the scenario pauses the page clock before the
enable click, so the highlight's `setTimeout(clearHighlight, 2400)`
cannot fire while the status poll runs. The comment claimed a reload
could outlive that expiry, and the note explained the two CI timeouts
at the untouched highlight locator the same way. The cause of those
timeouts is not diagnosed, so both now say so instead of offering a
mechanism the paused clock rules out. The pre-change file reproduces the
same hang under the same lane command, which the note records.
workspace-new-session-folding: `nth(7)` is the first row the batch
reveals, not the last established one, and the reflowing `showMore.click()`
is above the comment, not below. Both are corrected, and the workspace-row
comment names what `:hover` reveals instead of restating a cell swap the
project row does not have.
turn-tail-actions: clear the pointer modality flag with `Shift`, which
moves no focus, matching focus-rings.e2e.ts.
Master's 07dcdb46e5 moved the question step's settled title into the
`Ask question waiting` row of mid-steer.expected.md, so the button this
scenario waited for no longer renders. The wait therefore never resolved and
parked the scenario on the question page, producing the lane's cascade: `no
turn/end within 30000ms`, no live root session matching session.v4.jsonl, and
`llm-replay: fixture not fully consumed`. `captureStableAria` already polls
until two consecutive normalized captures agree, so the settled state is
captured without the extra wait.
DSH_SNAPSHOT=replay pnpm exec vitest run --config vitest.web.config.ts
apps/web/tests/steering.e2e.ts -> 7 passed (reproduced twice); with the wait
present the same command fails at its 10 s bound and again widened to 30 s.
Three scenarios captured an aria region while the state their golden
records was implied by the pointer, the keyboard, or an in-flight Host
read, so the capture could land on the other state.
workspace-new-session-folding captures two goldens of one sidebar, each
recording a hovered row that Rows.module.css renders with the row
actions in place of the relative time. Both hovered rows are now
established before their capture instead of inherited from wherever the
Show-more click left a stationary pointer. The serial self-hosted master
lane failed this scenario in every failing run observed between
2026-09-24 and 2026-09-27.
turn-tail-actions records a tooltip beside the focused Copy footer in
running.expected.md. Tooltip ignores focus while the last input came
from a pointer and only a keydown clears that flag, so the scenario now
establishes keyboard-owned focus and waits for the bubble.
plugin-install-cancel polls the enabled card's status instead of
sampling it once. The store marks the card and starts the directory
reload the Host answers, and the status flipped disabled -> running 61 ms
after the highlight in three local runs, which the single read sampled
inside.
Goldens, product code, and assertion strength are unchanged.
Extract settleRefresh() so the spinner-minimum hold has one settle point,
advance the e2e fake clock past the 400ms hold before asserting the button
restores, and drive the disposal-during-hold store test on fake timers for
both success and failure reads.
The install registry picker used MenuSurface and overrode the theme-owned
menu material tokens, which the shared-menu-material gate rejects. Return
the picker to a fieldset (its native group role) and paint an opaque
bg-layer-2 surface so the form controls stay readable without touching
menu tokens. This also restores the group's radios to the accessible tree.
Align plugin-install-registry.e2e and its snapshots with the PR's visible
text: registry option names drop the parentheses around the host, the
custom address field is no longer disabled, and the custom hint spells out
http(s) and ~/.npmrc.
Hold the manual-refresh spinner to a 400ms minimum so a fast read does not
flash it, clearing to idle (or the failure toast) only after the minimum,
and dropping a refresh that settles after disposal.
Prune the retired no-unknown-casts baseline entry for browser-plugin.client.spec.
Drop the plugin-manager refresh e2e to a single failure round: the store's
success and retry outcomes are covered deterministically by
manager-store.client.spec.ts, while the e2e keeps its unique value — real
500ms tooltip timing, a running spinner animation, aria-busy, and the
portaled failure toast over kept cards. Regenerate refresh.expected.md.
Remove the components spec tooltip-delay case; ui-primitives'
tooltip.client.spec.tsx already covers 499/500ms hover/focus delay, early
cancellation, and portaling. PluginManagerPage.tsx stays at 100% coverage.
Closing the provider editor confirms its write, but the browser directory
refresh lands after it, and the reopened editor copies the previous input
selection into a new draft — checkbox polling cannot correct a draft that
was already built. Run 36159502081 (job 108152262196) read a stale `true`
from that draft at models-settings.e2e.ts:395.
Wait for the saved-provider notice, which the refresh publishes, before the
third reopen. The adjacent reopen in the same test file waits on the same
notice.
The browser lane pins the browser to Asia/Shanghai, and formatMessageClock
prefixes clock.md / clock.ymd to the clock once a message's local calendar
day differs from the renderer's. Four scenarios left both clocks on the
wall clock while their message times are stamped during the run — a live
composer send by the Host, a `createdAt: 0` seed anchored from Date.now()
— so the job that began six minutes before local midnight rendered
`9/25 23:58` where their goldens record `{{clock}}` (run 36157253018, job
108144715986: steering, markdown-images, message-actions, and
reference-composer failed together, then passed in the next run).
Read WEB_FIXTURE_TIME on both clocks instead. Live-stamped steering suites
install pinHostClock(), a Date.now spy that advances from the anchor with
real elapsed time; pinBrowserClock() re-applies one frozen instant from the
anchor on an interval, because holding a single instant removes elapsed
time from the 150 ms process-title hold and the composer's 10 ms
composition guard, which then never elapse. Seeded scenarios anchor their
seed beside the pinned browser clock, which keeps the relative-age buckets
their goldens assert.
The steering mid capture waits for the settled question title its golden
records, and the whole-queue scenario waits for the running turn before its
queue gestures, whose decision resolves against an agent that reports busy.
Goldens, the Asia/Shanghai browser timezone, and product rendering are
unchanged.
The bundle no longer declares `dsh.bundle.rowSwitches`, so its page offers a
switch per row while it is on, as every other bundle does. The README records
that the three rows work only together, and the Agent Note records the
rejected lock.
With `dsh.bundle.rowSwitches: false`, `listPlugins` reports each row the
bundle inserts with the `bundle-switch` read-only reason, so
`setPluginEnabled`, the `plugin_manager` tool, and the Plugins page agree,
and the page draws no switch for such a row. `bundleRowSwitches` validates
the manifest field; `listBundles` reports a non-boolean value on the bundle.
`BundleInfo.rowSwitches` is removed.
The language-switch case now reads the Automation tasks row's English title
and description, and the English guide writes the UI label as plain text like
the rest of the guide.
The visible id chip and the accessible name now share one check for whether
the entry id adds to the title, so an include: id that matches the title is
left out of both. The loading skeleton's status region carries visually
hidden text so screen readers announce it. The settings-chrome browser
scenario expects the preset group open and enabled rows without a tag, and
its two ARIA snapshots are refreshed. The row-partner CSS comment states its
two-column assumption.
The Web composition no longer carries `time-context`, `schedule`, and
`ui-schedule`. The optional bundle inserts the three rows and depends on their
packages, as the other optional bundles insert theirs, so the Plugins page lists
them under the bundle; `dsh.bundle.rowSwitches: false` keeps the bundle's own
switch as their only control. The three packages gain localized titles and
descriptions for that list.
The 2026-09-16 host-schedule-storage note now names the optional experimental
bundle as what mounts Schedule, since the Web composition carries the row disabled.
The opt-in note's Decision drops the sentence the Problem already owns, and the
assembled-remote mock states why its Schedule answers stay declared.
Cross-link the archived decision the opt-in note supersedes, state the Loader's one
entry per id and last-declaration-wins behavior in the overlay and extraction
alternatives, scope the missing-row warning to profiles that mount no Plugins page,
and move the switch-on-but-ineffective case to the list-order paragraph. Assert one
top-level composed row per id and correct the stale assembled-remote comment.