observePullRequest used to reveal and focus the explorer pane, and the persisted focus leak then made ambient tab opens (sidebar agent clicks, reconcile auto-open) insert agent tabs into the explorer pane while the main pane sat empty.
PR detection now only background-adds the PR tab to the explorer pane, with no reveal and no focus change, so a tab the user already opened elsewhere stays where it is.
Placement policy is centralized in workspace-layout-actions.ts (resolvePlacementPane / insertNewTabIntoPane): explicit paneId wins, and ambient entity opens (agent, provider_subagent, terminal, draft, browser) reroute from the explorer pane to the primary pane. explorerPaneId is a required input so callers cannot silently fail open.
Pane-rail + affordances thread explicit paneId instead of focus-then-open.
The explorer companion pane retention commit (de9e85862) auto-opens the
setup progress tab into the hidden explorer pane. toggleWorkspaceExplorerPane
only seeded the working_diff (Changes) tab when the pane was completely
empty, so once the setup tab landed there, clicking the explorer toggle
could never surface Changes/Files again via that pane's default tab.
Also fixes two e2e testid locators (workspace-new-tab-menu-trigger) and
one unit test assertion (browser automation handler) that assumed a bare
pane root, which predates that same explorer-pane-retention change: the
workspace root is now always a group with a hidden companion pane, so
unscoped locators/assertions must target the visible/named pane.
* fix(app): preserve IME composition in form inputs
Form model publication was replaying changing default text into the native or DOM editing surface. Keep the editing surface authoritative, defer web publication until composition commits, and reserve resetKey changes for explicit text replacement.
* refactor(app): centralize text editing authority
Route every app text field, including the composer and plugin runtime, through one platform-owned editing primitive. Its type and runtime reject controlled value replay, its command handle owns programmatic replacement, and lint prevents raw React Native TextInput imports outside the boundary.
* fix(app): restore composer placeholder styling
Treat the appearance setting as the rendered base size so the type ramp scales predictably. Native starts one pixel larger while web and desktop preserve the authored 14px base, with legacy values migrated on load.
The editor was trapped below the resident webview's body-level paint plane in split layouts. Render it through the shared overlay root while tracking the owning pane's bounds.
* fix(desktop): keep element selectors available on loaded pages
Annotate element and Screenshot element returned early when a cached load signal said the guest was not ready, even if the current document was complete and executable. Let executeJavaScript own the readiness boundary and keep selector cleanup available in the same stale-signal state.
Extend the real Electron browser regression to reproduce the complete-document/stale-signal mismatch through both toolbar actions.
* test(desktop): await browser selector injection
Poll the guest selector state through the browser bridge instead of assuming injection completes within 250 ms. This keeps the Electron regression deterministic on slower CI hosts.
* Harden browser selector session lifecycle
* Keep selector E2E fixtures independent
* fix(desktop): harden selector and annotation lifecycle
* fix(desktop): keep selector PR focused
* refactor(desktop): own element selector lifecycle
---------
Co-authored-by: Daniel G. Kang <180234654+dgk-dev@users.noreply.github.com>
Co-authored-by: Mohamed Boudra <boudra.moha@gmail.com>
* fix(app): validate persisted client state
Persisted replica data could satisfy TypeScript through an assertion while missing fields required by renderers. Treat every AsyncStorage value as untrusted input, clear values that fail strict schemas, and version the replica DTO so incompatible caches cannot hydrate.
* fix(app): preserve validated legacy state
* fix(app): preserve validated preferences
Preserve supported legacy location fields and every shipped theme while keeping unknown persisted fields rejected. Align browser test seeds with the current preference shape so strict validation exercises the intended provider.
* fix(app): migrate legacy agent preferences
Released clients stored host/location fields and per-provider thinkingOptionId together. Migrate those exact strict bytes into current preferences without admitting unknown fields.
* fix(app): preserve drafts with legacy reviews
Accept the exact strict shape released for workspace review attachments so migration can discard that obsolete attachment without clearing every persisted draft.
* fix(desktop): preserve browser tabs across focus changes
Electron can replace a guest WebContents when a webview is reparented, which reloads the page and can race automation against a destroyed target. Keep each browser guest on one permanent paintable surface and make user and agent resizing share one canonical viewport.
* fix(desktop): recover browser surfaces after lifecycle changes
* fix(desktop): preserve resident browser lifecycle state
* fix(desktop): seed inactive browser viewport
* fix(ci): skip unrelated required checks cleanly
Matrix jobs had to start no-op runners to preserve interpolated required-check names. Static named jobs let GitHub report genuine skipped conclusions while keeping those names stable.
* fix(ci): gate changes to install patching script
* fix(ci): route cross-package test contracts narrowly
* fix(ci): keep routed contracts merge-blocking
* fix(server): exclude nested test utilities from builds
* fix(ci): cover app-owned native and browser domains
* refactor(ci): align tests with runtime domains
Own browser specs, desktop specs, and Electron-only app code by stable directories so PR routing does not depend on filename conventions. Keep desktop integration coverage inside the required desktop check.
* fix(ci): keep routing and desktop suites isolated
The routing regression must run before dependency installation, and desktop Vitest must not collect Playwright-owned specs.
* fix(ci): isolate desktop e2e runtime
* perf(test): isolate Playwright workers
* perf(test): parallelize Playwright within specs
* fix(test): keep Playwright file scheduling
* fix(test): share dynamic Metro fixture
* fix(test): preserve own-host restart fixtures
* fix(ci): preserve routed contracts after rebase
* Stabilize Playwright wheel-scroll assertions