Opening a workspace while its local creation is still pending no longer seeds an idle terminal that steals focus from (or drops) the agent launch: the creating event carries the new workspace id, activation during creation shows the existing "Creating worktree…" panel, and the workspace takes over when creation finishes. A terminal the user explicitly opens is kept and the agent gets its own tab.
Fixes#25807
Supersedes #25812
Co-authored-by: Eotel <49769982+Eotel@users.noreply.github.com>
An idle saved Claude account whose ~8h access token lapsed (Orca never refreshes Claude logins; the CLI does on its next run) keeps its last-good usage for up to 24h and reads "Updates when Claude runs" instead of "signed out". Logins that can't recover (no refresh token, expired refresh token, or a revoked 401) still ask to sign in again, and the status bar keeps its warning icon when shown usage is over 30 minutes old. Kept snapshots drop windows whose reset has passed.
Fixes#30289
Supersedes #31105
Co-authored-by: luijoc <96428056+luijoc@users.noreply.github.com>
Resumed Claude/Codex agents come back in the folder they were started in: resume records save the agent's starting cwd from hooks (kept within a session and across Claude /clear and compact), persist it, and resume as `cd <folder> && <agent> --resume <id>` when it differs from the workspace root. A deleted folder now fails loudly at the cd instead of silently resuming in the root.
Fixes#16960
Refs #15485
* fix(worktrees): one host's git status no longer rewrites a same-path twin's branch (#32918)
* fix(worktrees): match a twin through its runtime alias and persist its review clear on the same copy
* fix(worktrees): keep the id-only patch for a lone copy; background refreshes refuse an unplaced twin
* fix(worktrees): a twin's identity patch skips the id-keyed review clear instead of reaching its sibling
* fix(worktrees): count runtime provenance when finding twins; Source Control refresh refuses an unplaced twin
* fix(worktrees): a twin's branch switch clears its own push target and review link on its host
* fix(worktrees): a twin's review clear keeps its stale-refetch guard and relink check, scoped to its own copy
* fix(worktrees): a twin tombstone names its full owner, and detected rows are stamped before sanitizing
* fix(worktrees): each twin copy keeps its own review-clear tombstone
* fix(worktrees): a rename carries each twin copy's review-clear tombstone
* fix(worktrees): a copy's tombstone from its twin days still drives its clear retry
* chore(worktrees): name why the existing review-link cast is safe
* fix(worktrees): one resolver names the copy for every git-identity read and write
* fix(worktrees): a Create PR run refuses when its copy no longer resolves; local null stays local
* fix(worktrees): an unselected twin stays unplaced; a legacy row's guard survives its first stamp
* fix(worktrees): a Create PR run keeps the copy it pinned at start; a legacy row's relink drops its stamped guard
* fix(worktrees): key review clears by the full stamp the next listing gives the row
* fix(worktrees): fill only missing stamp parts when keying review clears
* fix(worktrees): stamp review-clear keys from repo registrations, not the focused host
* fix(worktrees): a local row keeps its review-clear guard when discovery stamps it local
* fix(worktrees): stamp review-clear keys from the sole repo registration first
* fix(worktrees): a runtime-hosted copy's owner key ignores backfilled provenance
* fix(worktrees): Create PR and post-generation refresh read the pinned copy's push target
* fix(worktrees): a lone copy's push target still answers when the host index misses
* fix(worktrees): the push-target fallback accepts only an unstamped lone copy for a named host
* fix(worktrees): only SSH copies borrow a registration's provenance for review-clear keys
A woken sleeping agent keeps its launch config (flags like permission mode, model, custom args) so a later Orca restart restores it with the same flags; consuming the sleeping record no longer deletes the pane's launch config (retirement and launch-token/session matching still govern it).
Supersedes #31756
Co-authored-by: Jinwoo Hong <73622457+Jinwoo-H@users.noreply.github.com>
After Wake the window paints the restored history and then blanks its viewport, so the new
shell's prompt starts on a clean screen. The daemon's own copy of the screen, seeded with the
same history, skipped that step. A later reattach (a reload, a second window, the phone)
therefore showed the new prompt beside the dead shell's last one: `p7/sub$ orca@awin:~/projects/p7$`
(awin verify-11). The seed now ends with the same viewport blanking the window writes, built by
one shared function, after the process-boundary ground, which still never moves the cursor.
Co-authored-by: Claude <noreply@anthropic.com>
* feat(ssh): list saved port forwards that couldn't start, with why, and let you remove them (#32484)
A saved forward whose local port is taken, or that keeps failing, stayed
saved but invisible, retried in the background with no way to remove it.
The port-forward lists now show each saved forward that isn't open, with
the last start error, and Remove deletes it by its mapping.
* fix(ssh): a forward whose ssh exits after starting backs off like a failed start
* fix(ssh): re-register the new forward channels, close a forward a restore reopened before removing it, and update IPC fixtures
* fix(ssh): a flapping forward keeps its backoff streak until it stays up
* fix(ssh): translate the saved-forward row, say Stopped for a forward that died, toast a failed remove, and reset backoff on manual edits
* fix(ssh): keep saved forwards listed and removable while the host is offline; label the remove button; test the healthy-uptime reset
* fix(ssh): mirror saved forwards from main with a stale-read guard, including hosts that never connect
* fix(ssh): scroll the offline ports panel when saved forwards overflow it
* test(e2e): give the saved-forward restart case's timeout its reason
Ctrl+Enter reaches Claude Code as CSI-u (`\x1b[13;5u`) on local Windows ConPTY panes, matching macOS/Linux, so custom ctrl+enter keybindings work.
Fixes#29711
Leave a user's node or python program alone after a Claude account switch; unchecked terminals get an accurate toast.
Fixes#30327
Co-authored-by: Adif Sgaid <69080239+adifsgaid@users.noreply.github.com>
* perf(lint): keep JS plugins out of default oxlint and adopt ast-grep
Any jsPlugin makes Oxlint hand every file's AST to Node, so the seven custom
plugins cost the default `oxlint` run several times its native time.
- Move app-store-performance, quadratic-buffer-concat, renderer-scrollbar-style
and ime-enter-guard (scope- or dataflow-aware) to config/oxlint-js-plugins.json,
run by `pnpm lint:js-plugins`, CI, lint-staged and the changed-lines gate.
- Port sort-comparator-performance, mobile-pairing and native-chat-design-tokens
to ast-grep rules with rule tests, and delete the plugins.
- Add @ast-grep/cli, config/sgconfig.yml, `pnpm lint:ast-grep` / `test:ast-grep`,
a CI step, and an ast-grep pass in check-changed-code-quality.
- Add flaky-test rules (wall-clock reads, real-time waits, polling loops,
unquarantined skips, unexplained timeout bumps), baselined by a count ratchet.
* Enforce flaky-test ratchet on changed files; refresh baseline
* docs(agents): keep custom-rule details out of AGENTS.md
* Resolve ast-grep from the repo so gate scans of other roots work
* feat(devex): component fixtures with headless screenshots for fast UI checks
Colocated *.fixture.tsx files export named fixtures (render + optional store
seed, setup, interact, width). A dev-only Vite entry (vite.fixtures.config.ts,
extending vite.web.config.ts) renders one fixture in isolation with the real
I18nProvider, TooltipProvider, theme class, default-seeded store, and the web
client's preload bridge (installWebPreloadApi) as window.api.
`pnpm fixtures:shot <id|glob> [--theme dark|light|both]` starts or reuses a
detached server (Vite + one headless Chromium) and writes PNGs to
.cache/fixtures/. A warm page re-renders in place (~100ms/shot) and reloads
only after source changes. A stdio MCP server (registered in .mcp.json)
exposes list_fixtures and screenshot_fixture.
Fixtures load only through import.meta.glob in the fixture entry, which is
not a production build input; oxlint rejects static imports of *.fixture.
* Keep fixture config and agent config inside reviewed root entries
* Baseline the fixture page's default-settings seed in the focus-read ratchet
* docs(agents): shorten fixtures pointer
* Allowlist the dev-only fixture explorer's labels in localization coverage
macOS: Option+Forward-Delete sends ESC d (delete word forward) for shells and inline agent TUIs, so Claude Code deletes a word instead of the whole line; full-screen non-agent apps (vim, nano, less) keep the native CSI 3;3~. Linux/Windows unchanged. Fixes#21491. Supersedes #21893.
Co-authored-by: bbingz <11211020+bbingz@users.noreply.github.com>
Windows + Codex: Ctrl+V, Shift+Insert and Edit > Paste send multi-line pastes with Alt+Enter newlines like right-click already does (restoring #14456 wiring lost in a refactor), so pasted lines no longer submit one by one when Codex's paste-burst heuristic doesn't catch them (e.g. disable_paste_burst = true). macOS/Linux clients unchanged. Fixes#30470. Supersedes #30550.
Co-authored-by: STiFLeR7 <157357792+STiFLeR7@users.noreply.github.com>
* fix(runtime): keep another paired runtime's terminals when one runtime republishes a shared workspace
Desktop B can mirror one SSH workspace from two paired runtimes: the host's
server, which runs its terminals, and desktop A's app, which hosts a browser
tab A opened there. Any frame from A's app replaced every mirrored terminal
row in the workspace, so B dropped the server's terminals until the server's
next frame re-created them. A frame now replaces only the mirrored terminals
and chats its own runtime owns, matching how browser and editor rows were
already scoped.
Fixes#31983
Reported-by: amara-black
* fix(runtime): retire an omitted host's rows without waiting on a sibling host's replay
A tombstone now retires only its own host's rows, so the visibility-resume
repair no longer has to hold it back until every surviving host has a frame
to replay over the wiped workspace.
* refactor(runtime): one ownership rule for rows a paired runtime's frame may retire
Bindings first, then the frames that last published the row, then the
workspace's own server; owners that were removed no longer count, so any
runtime's frame can retire their rows.
* fix(runtime): address review on cross-runtime mirror ownership
Drop the workspace-owner guess for rows with no evidence, let a frame that
publishes the same chat take its row over, and refocus a surviving runtime's
terminal when the active one is retired.
* refactor(runtime): a mirrored terminal stays while any other live runtime claims it
Union pane bindings and last-frame mappings into one claim set, so a row two
runtimes share survives one runtime's retraction. Drop the chat ownership
extension: chats are shared across runtimes by session id, and the reported
bug is terminals only.
* refactor(runtime): claims come from the frames that published a terminal
A runtime that stopped publishing a terminal no longer claims it through a
cached pane binding; bindings only speak before any frame this session.
Keep the visibility-resume repair's survivor deferral as it was.
* fix(runtime): bound pane resources decide a mirrored terminal's runtime; chats follow the same rule
A shared terminal whose bound runtime drops it is retired and rebuilt by the
other runtime's next frame, so it never streams from a runtime that left.
Chats another live runtime owns survive this frame unless it publishes or
replaces them.
* fix(terminal): close another paired runtime's terminal on that runtime
A workspace can now keep terminals another runtime publishes, so a close
follows the runtime the tab's own PTY names before the workspace catalog.
* fix(runtime): only this machine or a live paired runtime keeps a chat another frame omits
Keep the rules most UI changes need in docs/STYLEGUIDE.md (32.9KB -> 10.1KB)
and move per-component specs, native chat scale, UX rationale, and the
screen review rubric into docs/styleguide/. Values now point at main.css
instead of being restated. Lint messages and comments that cited moved
sections now point at the new files.
* fix(sidebar): keep the workspace hover card open behind link actions and system-browser opens
* fix(sidebar): scope the unfocused hover hold to external opens and fix chooser dismissal edge cases
* perf(sidebar): register the refocus listener only while a hover hold is set
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(worktrees): never leave the create-worktree dialog stuck on a stalled preflight
Bound the same-repo PR branch fetch with the review-head fetch timeout, and
hand a slow composer submit off to a staged sidebar row so the dialog closes.
* fix(worktrees): harden create-worktree handoff from adversarial review
Dedupe handed-off submits against linked creations resolved during preflight,
bound the staged sidebar row with a deadline, offer Reopen on handed-off
failures, and bound the GitLab MR branch fetch.
* test(worktrees): expect the bounded PR/MR branch fetch timeout
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* feat(managed-server): offer Update and Update anyway on a managed SSH server's row
A managed SSH server only updated on connect, and every connect deferred while
a terminal ran there. Its row now offers Update when this desktop bundles a
newer build than the Orca that runs the host. When running terminals defer it,
Update anyway restarts the server past them; the terminal daemon carries them
across the restart.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(managed-server): Update anyway passes only counted live terminals and keeps Update's version admission
Update anyway now sends the incumbent build it confirmed over; under the host
fence it is refused if another desktop activated a build since, keeps the
newer-host downgrade refusal, and never passes an uncounted terminal census.
The offer also covers hosts activated before the Orca version was recorded,
for the build this desktop already deferred there.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(managed-server): Update anyway also confirms a menu Update the row does not advertise
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(managed-server): the row shows one confirmation at a time
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* ci(e2e): route managed-server update sources to the Update anyway Docker spec
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* fix(worktrees): explain the shallow-clone local base warning and let users stop the refresh
When a shallow clone hides the shared history, the local base fast-forward
refusal now says so and shows the git fetch --unshallow command, instead of
blaming local-only commits. Every refresh-failure toast also gets a
"Stop updating local main" action that turns off Keep Local Main Up to Date.
* feat(worktrees): add an Unshallow button that runs the fetch in a new terminal tab
* feat(worktrees): rename the opt-out to Disable Check and explain it in a tooltip
* fix(worktrees): harden the Unshallow launch
Reuse createNewTerminalTab (owner-routed, no focus read) instead of a
parallel launcher, report real host-launch outcomes before dismissing,
only offer the button for shell-inert remote names, route activation by
the creation host, ignore double clicks, and treat only a silent exit 1
from merge-base as a missing merge base.
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(repos): drop a repo's worktree rows when its host's catalog no longer lists it
A paired server that relists its checkouts under new repo ids (a project
copied into another profile, then that profile switched to) left the
desktop showing the old rows beside the new ones, the old under Unknown.
Every catalog fetch now drops the rows of the repo registrations it
removed, scoped to the dropped host.
Refs STA-12076
* fix(repos): purge a dropped repo's workspaces like a vanished checkout
Clears selection, tabs and per-workspace state of the dropped rows through
the same bulk purge an authoritative scan uses, flushing delayed editor
text first; the three catalog fetch paths share one finalize step.
* fix(repos): drop rows only, never a registration added after the request
Review: purging tabs and selection could race repo add/remove and lose
state. Keep the change to rows, and fence it to registrations the store
held when the catalog request went out.
* test(e2e): drop the fixed settle wait from the repo id change repro
The daemon records each terminal's spawn pane key and reports it in session listings; the sleeping-agent activation gate uses it as an exact-match fallback to find a live agent PTY after the host re-keys its tab, so paired clients and phone wake no longer start a duplicate `claude --resume`. Fixes#21235, #24385. Supersedes #24386.
Co-authored-by: Minidoracat <11269639+Minidoracat@users.noreply.github.com>
Orca-driven Codex launches that type their prompt after start (workers, automations, continuation/handoff, pane-spawn and other submit-after-ready flows) pass `-c check_for_update_on_startup=false`, so Codex's "Update now" screen can't block them or be accepted by Orca's Enter. User-typed launches and resumes are unchanged. Fixes#15814, #18153. Supersedes #18156.
Co-authored-by: luisgonzaleznf <47302972+luisgonzaleznf@users.noreply.github.com>
* chore(lint): remove disable directives that suppress nothing
Each was reported unused by the oxlint config that owns its rule (root,
casting, react-doctor, anti-slop, native-plugins), including 17 file-level
react-doctor blanket disables whose rule no longer fires. An unused
react-hooks disable was masking a react/purity finding in
useAgentBucketCounts (compiler rules skip functions with hook
suppressions); suppress that one explicitly where the code explains it.
* test(tab-bar): use typed tab/file fixtures instead of @ts-expect-error partials
* chore(lint): fix misc suppressed lint violations instead of disabling
- consistent-type-imports: the "vi.importActual requires inline import()"
rationale was wrong; use typed vi.mock(import(...), importOriginal) or a
type-only namespace import.
- no-control-regex: source-control commit/push failure summaries, shell PATH
hydration, and ipynb ANSI parsing reuse the shared ansi-escape-sequences
stripper and control-character pattern instead of private copies.
- no-explicit-any / no-useless-spread / no-this-alias: typed `this` in
bridge test mocks, drop blanket file-level disables, drop a this-alias.
* refactor(main): replace avoidable type assertions with checked narrowing
17 production cast suppressions in main/wsl-guest removed: untrusted IPC
and RPC payloads (Jira issue update, WSL guest plugin install, handshake
type) now narrow through isRecord/type guards and build typed results;
redundant casts dropped; hand-rolled object checks and errno casts use the
existing isRecord / hasErrorCode helpers.
* refactor(renderer,mobile): replace avoidable type assertions with checked types
13 production cast suppressions removed: RPC failures read through the
`ok` discriminant instead of casting to RpcFailure; react-native-web host
refs narrowed with instanceof HTMLElement; redundant reply/diff casts
become checked annotations; the TUI agent param uses z.custom(isTuiAgent)
like its sibling schemas; the agent-status slice cast that hid missing
actions is gone so the creator's return type checks the whole slice.
* chore(lint): drop dead tiptap wrapper; scope anti-slop mocking rule to test support
- safe-react-node-view-renderer patched handleSelectionUpdate, which
@tiptap/react 3.31.3 no longer has, so it always returned early. Use the
stock ReactNodeViewRenderer.
- The anti-slop test override only matched *.test.{ts,tsx}, forcing
file-level no-module-mocking disables in test harnesses and fixtures.
Match *-test-harness, *-test-fixture(s), .test-fixture and *.test.mjs,
then drop those disables and the now-redundant per-file exemptions.
* test: replace repeated cast suppressions with typed fixtures (main/shared/e2e)
Net -119 cast directives. Typed fixtures replace recurring casts:
unusedSshConnection/unusedOrcadHostTransport placeholders, a typed
readPersistedState harness read (exposed a test treating optional
httpProxyUrl as always set), real Response objects in fetch spies, an
updater main-window fixture, a CDP webContents adapter, one shared
releaseModuleExport for cross-version tests, and requireOverlayDir in
place of `as string` on possibly-undefined overlay paths.
* fix(runtime): validate loaded device rows and host scope instead of casting
Two cast suppressions claimed checks that did not happen:
- device-registry-file-read: "normalizeLoadedDeviceRow re-validates every
field" was false; deviceId, token, name and timestamps were never
checked. Rows without an identity are now dropped and the file is set
aside through the existing corrupt-file path (registry reads as
doubtful, so no revocation verdicts); a non-string token is stripped
so the row stays listed but cannot authenticate.
- client-ui: "only optionality differs" was false; any string reached
persisted workspaceHostScope. Narrow it with isWorkspaceHostScope and
ignore unknown scopes (wire schema unchanged for mixed versions). The
remaining cast now states what it actually covers.
* test(runtime,ipc): drop redundant cast suppressions; typed runtime store fixture
-98 cast directives across runtime/ipc tests: 61 casts that typecheck
without the assertion; a typed vi.fn for scoped invalidation; a
partialRuntimeStore fixture in place of `store as never` for
OrcaRuntimeService; typed worker terminal/authority builders in the
incarnation-fallback suite (the old stub lacked runtimeId, ptyId,
worktreeId and launchTokenHash) plus removal of stacked duplicate
directives there.
* chore(lint): typed vi.mock imports and explicit listener snapshot in shared-bucket tests
Remaining files from the misc-rule pass that overlapped other buckets:
codex-accounts mocks use the typed vi.mock(import(...)) form instead of
a consistent-type-imports disable; the daemon late-exit fixture copies
its listener set with Array.from instead of a no-useless-spread disable.
* refactor(github,mobile): narrow routing settings type; drop remaining avoidable casts
- work-item-routing cast a one-field object to AppState['settings'] under
a claim that readers only consume activeRuntimeEnvironmentId. Return the
existing RuntimeFocusSettings pick instead and narrow the cache-key
chain's parameters, so the compiler enforces that claim.
- mobile file preview reads refusals through the RpcResponse `ok`
discriminant rather than a cast.
- terminal document host-notify accepts `unknown` errors, removing the
message-bridge cast.
- native chat readability drops a redundant repo-list cast.
* test(renderer): replace repeated cast suppressions with typed fixtures
-140 cast directives (and ~150 unsuppressed casts of the same shapes):
- mockPanePtyArgs owns the connectPanePty fixture casts, so a wrong
argument order or fixture is now a type error; drop binding casts for
members PanePtyBinding already declares.
- Seed AppState tab rows with the typed makeTerminalTab; this exposed a
split direction 'row' that is not a TerminalPaneSplitDirection.
- Partial GlobalSettings use getDefaultSettings overrides; Jira-only
stores use createTestStore; runtime environment rows/statuses come from
a typed paired-runtime-environment fixture.
* chore(lint): drop no-module-mocking disable now covered by the test-fixture glob
* test(e2e): fix all 221 grandfathered e2e type errors; baseline to zero
The e2e typecheck baseline grandfathered 221 errors in 119 files. About a
third were tests drifted from the current API, several of which would
fail or mislead at runtime:
- terminal-shortcuts called a nonexistent xterm buffer method;
- the daemon-lifecycle fake lacked terminateOwnedTree, hidden by importing
its type from a module that does not export it;
- arguments in the wrong position (listProcesses(hostId, opts), a dropped
pty.write inputKind, an extra toWebTerminalSurfaceTabId argument);
- removed enum values ('drawer', 'idle', 'editor',
'session-insecure-origin') and a launch_source the telemetry schema
rejects;
- missing required fields (turnScope, childWorkOpen, providerIdentity,
reviewLookupOutcome, runtimeServiceAccount behind an `as RelayConfig`);
- dead reads (tab.customTitle, bucket.targets, changedPixels).
Also lifts variables that narrowed to `never` after poll-callback
assignment, and adds a typed xterm render-dimensions accessor. Includes
the cross-version suites' shared releaseModuleExport adoption.
* chore(typecheck): gate the now-clean e2e typecheck in local lint
The e2e baseline is empty, so the ratchet now rejects any tests/ type
error. CI already runs it via run-ratchets; add it to pnpm lint's
explicit ratchet list and replace the stale 'not yet clean' note.
* test(mobile): typecheck every mobile test; baseline 105 files -> 0
mobile/tests-typecheck-baseline.txt grandfathered 105 test files (531
errors) whose types CI never checked. Contract changes had landed without
these tests following, and type pins in them silently checked nothing.
- Root cause: salvagedOptional (src/shared/zod-salvage.ts) cast away
zod's optional marker, so every reply type built on it claimed
salvaged fields were always present. Return a real ZodOptional;
runtime is unchanged (the outer optional short-circuits undefined).
- Production now takes the port it uses: RpcOperationSender instead of
RpcClient for send-only callers, a narrow fetch signature, and only the
request fields push-receive reads.
- findAllByHostType/findByHostType replace `'Text' as never` lookups;
stubRpcClient gives tests a typed RpcClient.
- Real test bugs: settings-host-client-lifecycle "relay" hosts were
direct hosts (relayHostId no longer exists); two toEqualTypeOf pins
could never hold; a recovery hook ran with an undefined scope key;
a create-PR test cast to a never-imported type; "no URL" assertions
passed on null; plus stale required props, args and enum values.
* test(e2e): parse workerStart result instead of casting it
* test(runtime): typed partial runtime stores with production-shaped fixture data
Route every partial store passed to OrcaRuntimeService through
partialRuntimeStore with typed row builders, removing 60 cast directives
that hid fixture data production never produces:
- paired-client-navigation-test-harness keyed setWorktreeMeta writes by
`[object Object]` (it took an id where the store takes a target), so
reads never saw them;
- invalid worktree lineage origin/capture and incomplete workspace lineage;
- getWorkspaceSession returning null where the store returns the default
session; getGitHubCache returning undefined;
- orcad migration states missing migrationId/manifest/receipt, with a
toEqual pinned to that incomplete shape;
- partial repos, metadata, folders, project groups and notification rows.
* refactor(renderer): resolve suppressed React hook lint instead of disabling it
109 of 187 react-hooks/react-doctor suppressions fixed:
- ~95 exhaustive-deps disables only hid stable refs and setters passed
through controller objects; list them (identity never changes, so
nothing re-runs), plus a few real stale closures (tabId/worktreeId).
- Effects keyed on one value that read the latest props now use
useEffectEvent (WebConnect, skill freshness, Bitbucket dialog seeding,
Landing preflight, automation catalog, structured-session recovery,
file explorer).
- The conditional layout-owner hook is selected once at module load from
the build constant instead of a rules-of-hooks disable that also hid
the whole controller from compiler and react-doctor rules.
- A hook disable was masking a react/purity Date.now() in the palette's
recent-tabs memo; sample it when the snapshot is captured.
- Three identical debounced-search effects share
useTaskSearchDebouncedApply; layout snapshot merge helpers move out to
stay under max-lines.
Remaining suppressions are mount-once resources, invalidation epochs,
deliberately keyed fields, and test harnesses that mock the dispatcher.
* chore(ratchet): exempt test harnesses and fixtures from the focus-read ratchet
The ratchet skipped *.test-fixture.ts but scanned *-test-harness,
*-test-fixture(s) and *.test-support files. Those seed settings with
getDefaultSettings (which the ratchet discovers as a focus reader) and
never route host work, so 17 baseline entries were false positives, and
replacing a test's `settings: {...} as never` with typed defaults tripped
it. Match the repo's test-support names and prune (which also drops one
stale entry), and rename the frame-ordering harness to *-test-harness like
its siblings. A blanket "-harness" match would also exempt
e2e-terminal-pty-harness, which production imports, so the names stay
explicit.
* test: merge duplicate shared/constants imports
* revert(file-explorer): restore original tree load effects pending SSH e2e investigation
* test(runtime): drop RuntimeStore cast now that the fixture is typed
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* fix(terminal): keep keys typed into a starting agent pane as an unsubmitted draft
* fix(terminal): keys typed after the bind always go live; never paste over a driven screen
* test(terminal): a deferred split with late startup work drops its held keys
* fix(terminal): capture only before the first bind; drop drafts on attach or reattach binds
* fix(terminal): leave paired-server panes' type-ahead to their own transport
Devin launch prompts are delivered as argv (`devin [flags] -- '<prompt>'`, Devin's documented interactive-prompt form) instead of typed after start, where they were lost before Devin was ready. Fixes#26302. Supersedes #26387.
Co-authored-by: ggbdpq <67929842+ggbdpq@users.noreply.github.com>
The suite loaded the main runtime graph in beforeAll; run beside the rest of
terminal-pane at six workers, that cold load passed the 60s hook default and
failed the suite before any case ran. Loading it at module scope, as other
terminal-pane suites do, takes it off any per-hook timer.
Claude AskUserQuestion with option previews: Orca's ask answers now send digit (focus wiggle for option 1 with a note), optional `n`+note, then Enter, since a digit only moves the highlight in Claude's preview layout. A bare digit on a preview question no longer clears waiting. Fixes#16865.
Co-authored-by: Frederic Barthelemy <209712+fbartho@users.noreply.github.com>
* fix(terminal): keep keys typed before a pane's PTY exists
* fix(terminal): hold pre-spawn keys only in panes with no startup work
* fix(terminal): no pre-spawn hold for live-entry resumes or main-started panes
* fix(terminal): keep the deferred-split hold exactly as before
* fix(terminal): drop held keys if a resume or startup work appears before the bind