* 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>
* refactor: group local and WSL filesystem watchers
* test: isolate startup ordering from watcher initialization
* test: collect the startup preflight subject once
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>
Orchestration mailbox pointers now get their delayed Enter for Cursor too (the Cursor no-Enter skip from #3156 left mail delivered but never acted on). Verified with real cursor-agent. Fixes#19760. Supersedes #19761.
Co-authored-by: Maple <54388301+zleo-ai@users.noreply.github.com>
* fix(windows): respawn a ConPTY pane that started without an input reader
Bundled ConPTY occasionally starts a pane whose console host never reads
its input pipe: the shell prints its banner and prompt, but every keystroke
and the typed launch command are dropped. Such a pane is identifiable from
its first output, because a healthy one always queries DA1 before relaying
anything else. The daemon now waits for that first output, respawns once
when it is missing, and replays the buffered startup output so no byte is
lost. The table stress probe now waits for node-pty's own readiness rather
than typed input.
* fix(windows): bound the ConPTY startup check and never re-run a command
- Respawn only when no command rode in the shell's argv; otherwise keep the
pane and say on screen that it cannot take input.
- Accept the pane unverified after 5s of silence instead of stalling the spawn.
- Distrust the check after two misses with no pane ever announcing, so a
changed ConPTY build cannot respawn every pane.
- Kill a discarded pane through its job before closing the pseudoconsole.
- Report a cancel during the wait as an attach cancel, not a spawn failure.
- Assert on real Windows ConPTY that a fresh pane announces its input reader.
* fix(windows): respawn only Orca's own shell argv and close an unready pane now
- Treat user shell args like an argv startup command: never respawn, show
the notice instead, so a command such as pwsh -Command never runs twice.
- Close a discarded pane's pseudoconsole directly; node-pty defers kill()
until first output, which a silent pane never produces. Kill the shell by
pid when no job owns it.
- Reconcile a master node-pty retired during the startup wait.
- Drop the test-only respawn counter; the count rides in the daemon log.
* fix(windows): tear a discarded unready pane down through node-pty's agent
node-pty defers kill() until first output, so closing only the native
pseudoconsole left the input pipe and output worker of a silent discarded
pane alive until daemon exit. The agent's kill closes all three.
* fix(windows): discard startup panes by handle only; account for the mock prelude
A pid-addressed kill could hit a recycled pid when the shell exited during
startup; the job and the agent's native kill both act on handles. Lifecycle
assertions now expect the replayed DA1 query when the suite runs on Windows.
* test(e2e): give the mocked Windows pane its ConPTY prelude
* 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
Windows Codex: when a large dispatch/worker-start/agent prompt paste is still held in Codex's composer (Codex reads the paste as key events and treats an early Enter as a newline), Orca re-presses Enter only while the composer visibly holds exactly that pasted prompt, re-checking turn start, permission and the deadline right before each Enter, within the worker-start budget. Non-Windows unchanged except the blind launch retry is skipped once a turn starts. Fixes#11343.
OpenCode 2.x model-selection launches require 2.0.19+ (upstream #51938 waits for agent/model before auto-submitting --prompt) or a native-prompt version (2.0.12, 2.0.16), fixing a regression from #33307 where 2.0.14/15/17/18 accepted a picked model but silently dropped the first prompt.
Windows Claude hooks for profile paths with a space register in Claude's exec form (orca-agent-hook.exe + args) instead of the slow encoded-PowerShell fallback (~650ms → ~15ms per event); the script path in args lets older Orca builds recognise and replace the entry. Older Claude keeps the PowerShell launcher. Fixes#19187. Supersedes #19255.
Co-authored-by: marcdhansen <7607160+marcdhansen@users.noreply.github.com>
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.
Hermes: after the pane's turn ends, tool hooks from Hermes' hidden background memory/skill review are ignored until the next pre_llm_call, so a finished pane no longer flips back to Working. Delegate children (platform: subagent) don't end the parent's turn; approvals are always admitted. Fixes#31798. Supersedes #31807.
Co-authored-by: Matt Bass <76124126+mattjax16@users.noreply.github.com>
Linked worktrees now get a per-worktree userData profile under
orca-dev-wt/<hash>, a deterministic collision-checked renderer port
(15200+) and CDP port (9333+), so parallel `pnpm dev` runs no longer hit
the single-instance lock. The main checkout keeps the shared orca-dev
profile; --shared-profile / --worktree-profile / ORCA_DEV_PROFILE override.
The runner prints `ORCA_DEV_INSTANCE {json}` on stdout and mirrors it to
out/dev-instance.json so agents can attach over CDP. orca-dev.mjs and the
terminal wrappers follow the profile the worktree's dev app used.
Adds profile seeding (pnpm dev:seed-profile, pnpm dev --seed-profile,
pnpm dev:fresh --seed-from): an allowlisted slim copy of projects,
workspaces, settings, SSH targets, and the account session, written as
orca-data.json for Orca's own import; the source is only read.
Storage reclaim removes per-worktree profiles once their worktree is gone
and no app, runtime, or daemon still uses them. dev-fresh-profile.sh is
replaced by a cross-platform dev-fresh-profile.mjs.
* 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>
Hermes home resolves like Hermes itself on Windows (HERMES_HOME, else %LOCALAPPDATA%\hermes, else a legacy ~\.hermes that alone exists), shared by hook install/status, skill discovery and session reading; session reading also honors HERMES_HOME. Fixes#24966. Supersedes #25058.
Co-authored-by: innocarpe <2222333+innocarpe@users.noreply.github.com>
Kimi Code workspace pre-trust: Orca writes Kimi's own trust record (<KIMI_CODE_HOME or ~/.kimi-code>/workspace-trust/wd_<slug>_<hash>) for the exact launch folder, so Kimi's "Trust this folder?" menu no longer swallows Orca's prompt. Never pre-trusts the home folder or a filesystem root. Fixes#26680. Supersedes #26738, #26741.
Co-authored-by: Naveen Chatlapalli <54518943+1aifanatic@users.noreply.github.com>
* fix(mobile): stop losing phone terminal output sent as output spans
Hosts sent stripped/transformed terminal output to terminal.subscribe as
OutputSpan (opcode 15), which no phone has ever decoded. The phone dropped
it silently, e.g. the reset after a killed full-screen program, leaving
the phone stuck on the alternate screen.
- Host: terminal.subscribe sends spans as plain Output text; only the
desktop multiplex (which decodes spans) keeps OutputSpan.
- Mobile: decode OutputSpan for older hosts, use the shared protocol
table, and end the stream with a logged error on any frame it can't read.
* fix(mobile): throttle unreadable-frame resets and log once per stream
* fix(mobile): reset a throttled stream after the window; forget released stream ids
* fix(mobile): defer throttled unreadable-frame resets instead of dropping them
* 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(terminal): a slept WSL pane on Windows wakes in the folder its shell was in
Bash in a WSL pane on Windows reports no folder (no OSC 7), so Sleep's final checkpoint kept the
folder the pane was spawned in, and Wake reopened every such pane at the workspace root. Before
that checkpoint, the daemon client now reads the pane shell's folder inside its distro, from
/proc, by the ORCA_PANE_KEY its processes inherit, and stores it in the checkpoint Wake restores
from. The read is one fixed script per distro per Sleep batch, shared while in flight, capped at
3 s and a quarter of the Sleep's remaining budget. It only runs in a distro that is already
running. Any failure keeps today's spawn folder.
After an app relaunch, the client learns each live WSL session's distro and pane key from the
sessions it adopts at startup. A pane slept before the window reopened it therefore keeps its
folder too.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(terminal): read only Orca pane processes, skip the running check for live panes, wake at the asked folder when the kept one is gone
Review of #33398:
- Speed: the guest script forked about a dozen times per /proc entry (2.3 s for 405 processes),
so busy distros spent the whole 3 s budget. One `grep -l` over every environ now picks the
Orca pane processes first, and only those are read, so the cost no longer grows with the
distro's process count. A test counts the tool calls for 5 and 400 other processes and
expects the same number.
- Fewer launches: a live pane's own wsl.exe keeps its distro running, so a Sleep of a live pane
skips the `wsl --list --running` check and costs one launch per distro instead of two.
- Deleted folder: a pane whose kept folder is deleted before Wake failed to spawn when the
guest could not answer the folder probe. Wake now retries once in the folder the pane asked
for, as before the folder was kept.
- Drops the unused sid and argc fields.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(terminal): skip the running check only for a distro last seen running; keep the asked folder through the restore re-spawn
Review of #33398:
- A live pane skips the `wsl --list --running` check only when its distro is also in the last
confirmed running list. The client can still hold a session as live after its distro was
terminated or across a daemon disconnect, and reading that distro would boot it.
- The probe-to-create race re-spawn now reads back the folder the spawn actually used, so after
the deleted-folder retry neither `initialCwds` nor the cold-restore payload keep the gone path.
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <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>
Claude worker-start also becomes ready when Claude's empty composer (❯ + NBSP, only after Claude's DECSET 2031) draws, racing the title-based tui-idle wait, so worker-start no longer times out when Claude's terminal title is disabled. Fixes#22655.
* 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
* fix(daemon): contain ConPTY output-relay write errors to the one terminal instead of killing the daemon
* fix(daemon): end a Windows terminal whose output relay dies before connecting
* fix(daemon): kill a Windows terminal whose output relay fails before its first output
* fix(agent-teams): run the tmux shim through the headless host's CLI, never the GUI binary
On an orca serve host the runtime has no Electron resources directory, so the
shim fell back to PATH and picked the AppImage's Electron binary, which booted
Chromium and died on the SUID sandbox. Prefer the host's profile-scoped CLI
launcher, and route agent-teams-tmux through the packaged CLI redirect so a
direct Electron invocation never initializes the GUI.
Fixes#33354
* fix(agent-teams): prefer a fresh CLI over a shim bin inherited from an older leader pane
Orchestration mailbox pointers (typing and the delayed Enter) are held while a Claude pane shows an open dialog (AskUserQuestion / permission / plan approval chrome on screen, or a fresh waiting row when the screen can't be read), capped at 10 minutes, so mail no longer answers the dialog. Recheck backs off to 2s while held. Fixes#21745. Supersedes #26928, #21880.
Co-authored-by: yasyf <709645+yasyf@users.noreply.github.com>
The detached ssh-agent proof waited 10s for eval "$(ssh-agent -s)" to print
its pid. On a loaded runner the shell had already accepted and started the
command (OSC 133;C) when the bound tripped; the leading terminal queries are
the pane's normal prelude, not a missing marker. Across eight instrumented
CI runs the marker took 0.5-1.1s, so keep waiting on the marker but cap it
at 30s.
The ConPTY job-ownership proof matched ORCA_GC=<pid> against each onData
chunk, and once gave up after its 20s poll with no pane output to show why.
Two causes are possible: a chunk boundary inside the marker, which per-chunk
matching never recovers from, or a pane that trails the preceding table
stress test. Match the accumulated output and require a non-digit after the
pid, bound the poll at 40s, and include the pane tail if it still fails.
A complete XTVERSION reply is delivered immediately (past the daemon shell-ready gate) and echo-filtered like colour/private-DSR replies, so fish's startup XTVERSION reply no longer leaks into Codex's composer. Fixes#16489. Supersedes #16491.
Co-authored-by: Luis Urrutia <3771248+LuisUrrutia@users.noreply.github.com>
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>
Codex workspace trust is also written to the launch CODEX_HOME (managed-account home) at spawn, and native Codex chats pick their launch home before writing trust, so managed-account Codex launches no longer stall on "Trust this folder?". Fixes#27235.
Local (in-process) PTY provider: an immediate close now SIGTERMs descendants and waits up to the shutdown grace before SIGKILL (same as the daemon path since #22232), so Claude's SessionEnd hooks run. Refs #23865, #22039. Supersedes #24314.
Co-authored-by: innocarpe <2222333+innocarpe@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>
Native OMP chat (ACP): when OMP reports its session was closed (-32603 "ACP cancel cleanup timed out" / "Unsupported ACP session: <this session>"), Orca replaces the child so the next send or Retry reloads the session instead of failing forever. Other ACP agents unchanged. Fixes#32684. Refs #32161.
* 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