The PR test LoC job fetched .github/scripts/pr-test-loc-*.mjs from
pull/<n>/head and ran them with node while holding a GITHUB_TOKEN scoped
pull-requests: write, so PR-authored code executed under a write token.
Pin the fetch to the repository default branch. base.sha is not enough:
for stacked PRs it is an unreviewed feature-branch commit any collaborator
can push to, while main is gated by branch protection.
Also pass event data via env instead of shell interpolation, and add
set -euo pipefail so a failed download cannot leave a truncated script.
The managed hook command embedded a bare $SYSTEMROOT. Grok loads Claude's
settings.json hooks and statically prechecks env vars across the whole command
string, so the reference inside the never-taken Windows branch made it refuse
the hook on macOS on every event:
hook not executed: required env var(s) not set: ${SYSTEMROOT}
Grok fails these open, and Orca installs Grok's native hook separately, so no
status was lost -- the symptom is a swallowed failure line per tool call.
$VAR and ${VAR-} expand identically in POSIX shells absent set -u, so this has
no execution-time effect; only the static precheck observes it. Verified in Git
Bash on Windows that both guard forms resolve identically ($SYSTEMROOT is set
there and uppercase is the correct spelling -- $SystemRoot is undefined).
Also converts the three bare $HOME references so the regression test can assert
zero bare variable references with no exemption. A $SYSTEMROOT-specific check
would not have caught this class of bug being introduced elsewhere.
* refactor: split PullRequestPage.tsx under 400 lines
Move the 5888-line PR page into nested domain modules under
src/renderer/src/components/pull-request-page/ and leave a thin public
barrel. No intentional behavior change.
* rm plan
* Improve React stability and remove manual ref caching
- Stabilize React keys in CheckDetailsPanel using content fields instead of array indices to prevent unnecessary remounting
- Remove manual ref-based entries cache in PRFilesCombinedDiffViewer, rely on useMemo dependency (diffEntrySignature) instead
- Move sectionsRef assignment to useLayoutEffect to avoid render-phase ref writes
- Refactor usePRFileSectionLoader to destructure args for readability
* Improve PR page stability: add error handling and fix race conditions
- Add error handling with user feedback (toast notifications) for comment submission, review comments, diff loading, and file view syncing
- Internationalize hardcoded strings for PR state labels and error messages
- Fix race condition in reviewer submission by using a ref-based guard instead of render-time state
- Fix scroll restoration to avoid overwriting target positions with intermediate clamp values
- Add effectiveRepoId parameter for proper repo context in review operations
- Disable reviewer picker during submission to prevent concurrent requests
* Improve PR page stability: add timeout and stable list keys
- Add 45s timeout for diff loading to prevent indefinite hangs
- Fix React list key generation for annotations/jobs using content-based keys with occurrence tracking
- Refactor scroll position caching to properly handle mid-restore teardown
- Replace interpolated error messages with full locale-specific strings for close/reopen actions
* Fix PR diff viewer cache isolation and list key collisions
- Changed list key generation from string concatenation to JSON serialization to avoid collisions with actual content keys
- Added host-aware scoping to diff view caches so local and remote execution don't share entries
- Optimized virtualizer keys to use lightweight revision counter instead of full serialized signature
* Extract PR file state into entry-scoped hooks
Replace manual state resets with custom hooks that automatically clear section heights and active section when switching PR entries. This prevents state leakage between files and simplifies the diff viewer component. Also validates the active section key exists before passing it to child components.
* Improve PR page error messages, accessibility, and stability
- Show actual error messages from failed operations instead of generic fallbacks
- Add aria attributes for combobox/listbox patterns and proper option identifiers
- Consolidate duplicate label/assignee update logic and fix event listener passive mode
- Memoize GitHub source runtime to prevent stale closure in checks callbacks
- Extract filled state badge tone for reuse and fix workspace attachment type
- Guard textarea shortcuts against concurrent saves and add mention query test
* Add explicit PR file content cache eviction
Extract PRFileContentRequestArgs type and create evictPRFileContentRequest
function to handle cache eviction explicitly. Call eviction on load timeout
so retries fetch fresh content. Adds tests for cache behavior.
* fix(agent-hooks): make the Windows managed hook survive Claude-hooks-compat consumers
`~/.claude/settings.json` is not read only by Claude Code. Third-party
Claude-hooks-compat layers (cursor-agent, Devin) import the same file and
reimplement hook execution, so Orca's entry has to survive consumers that
support strictly less than the documented schema. Three separate defects
came from assuming otherwise.
1. The entry depended on `args`, which a compat consumer ignores.
`args` is valid Claude Code syntax, but cursor-agent spawns `command`
alone -- so `conhost.exe` ran bare, which opens an interactive console
that never closes. Hook payloads were typed into those stranded shells
(#14815). The entry is now one self-contained `command` string that
depends on nothing optional.
2. `conhost.exe --headless` never relayed anything. It implements the
ConPTY server protocol, not a generic no-window wrapper: it does not
wait for the hosted process and relays neither exit code nor stdout.
Measured directly -- `conhost --headless cmd /c "echo X& exit /b 42"`
yields empty stdout and no exit code, while the replacement returns
both and waits. So every hook was fire-and-forget, and whatever it
printed was discarded. Replaced with `-WindowStyle Hidden`, which
suppresses the window and keeps wait/exit-code/stdout intact.
3. The hook never wrote anything to stdout. Guards exited silently and
curl's output went to nul. Claude Code documents empty stdout as "no
decision", but cursor-agent treats PreToolUse as a permission gate,
fails to parse empty stdout as JSON, and blocks the tool call -- so
every shell command in every cursor-agent session on Windows failed
(#14818). The script now writes `{}` first, on both the Windows and
POSIX branches, which is documented to be identical to writing nothing
for real Claude Code. Gemini and Antigravity already did this.
Defects 2 and 3 are causally linked: `{}` cannot reach any consumer while
conhost is swallowing stdout, so neither fix works without the other.
Also fixed while establishing the contract:
- The launcher's own missing-script fallback returned empty stdout,
reproducing #14818 whenever `~/.orca` was cleaned or an install was
half-finished. It now emits `{}` too.
- PowerShell serializes progress records to stderr as CLIXML when stderr
is redirected; a consumer merging stderr into stdout would see those
bytes before the JSON. Every encoded payload now silences progress.
- `runtime-home-hook-command.ts` built its own launcher without window
suppression -- exactly the drift #14815 asks to prevent. All launcher
construction now goes through `windows-powershell-hook-launcher.ts`, so
the switch list cannot be present in one installer and missing in
another.
- Renamed `usesWindowsHeadlessHook` to `usesWindowsPowerShellLauncher`;
nothing is headless anymore, and the flag selects a launcher.
Testing: the new regression test asserts the effect a consumer observes
-- it runs the exact `command` string from settings.json through both
cmd.exe and Git Bash, across the guard-exit, reached-curl, and
missing-script paths, and parses stdout. Verified it fails when
`conhost --headless` is reintroduced. The previous tests all asserted
installer intent, which is why they passed through all three defects.
* fix(agent-hooks): close hook launcher review gaps
---------
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* refactor: split GitHubItemDialog.tsx under 400 lines
No intentional behavior change.
* refactor: group github-item-dialog into lifecycle folders
Reorganize the 50 flat files under src/renderer/src/components/
github-item-dialog/ into six lifecycle folders:
load-item-details/ shared types, both caches, fetch/settle, state badge
open-dialog/ dialog shell, headers, body, tabs, link copy
discuss-item/ conversation tab, comments, composer, timeline
edit-item-fields/ GH edit section, labels, assignees, status
inspect-pull-request/ combined diff viewer, checks tab
land-pull-request/ PR actions, merge menu, reviewers
No intentional behavior change. All 50 files moved verbatim; the only
edits are relative-import specifiers (sibling paths plus a depth bump
for ../../../../shared) and the hardcoded module paths in the two
source-boundary tests.
Import graph stays acyclic: zero mutual folder pairs, no file importing
4+ sibling folders, no dest file importing the public barrel, and no
per-folder index barrels.
* refactor: split item references and improve diff-viewer remount logic
- Break down full `GitHubWorkItem` props into discrete `itemId`, `itemNumber`, and
`itemRepoId` in mutation and action functions to prevent over-memoization of callbacks
and improve dependency clarity.
- Extract `getPRFilesCombinedDiffSignature()` and use it as a component key to safely
remount the diff viewer when the PR revision changes, replacing generationRef tracking.
- Add `getKeyedCheckAnnotations()` and `getKeyedCheckJobs()` to generate stable,
collision-resistant keys for check arrays that may contain duplicates.
- Consolidate interpreter timeouts into a single `SPAWNED_INTERPRETER_TIMEOUT_MS` constant
and apply it via describe options rather than per-test values.
* refactor: improve github-item-dialog repo context and i18n coverage
- Add repoId prop to ConversationTab for explicit repo context override
- Internationalize UI strings in diff viewer and PR action components
- Improve error handling with cache rollback and guard cleanup on sync failure
- Enhance cache key validation for cross-window invalidation by repoPath
- Add repository access validation before rendering diff viewer
- Fix cross-platform issues: skip symlink test on Windows, normalize CRLF in test assertions
* Refactor check button i18n key and update text
- Replace hash-based key with semantic name for maintainability
- Change button label to "Open in browser" for broader context
Backspacing over the last radical of a Cangjie composition empties the IME's
marked text without reaching compositionend, and the vendored xterm
CompositionHelper only dropped the overlay's `active` class there. The box
stayed painted with whatever glyph it last held (#11951).
Clear on the state rather than on the key, as native terminals do: an empty
`compositionupdate` now hides the overlay instead of only ever showing it, and
a key the IME swallows re-derives the preedit from the textarea once it settles
so a composition emptied with no composition event at all is cancelled too.
Orca had to guess which shell would parse a queued command line, then emit
syntax for it. Guessing is unreliable for a remote or WSL host, and every
dialect-dependent function is a place to get it wrong.
Replace the guess. Everything emitted for a Unix shell is now built to be
correct in sh, bash, zsh, dash, ksh and fish alike, so no detection is needed:
- quoteStartupArg emits backslashes as "\\" and apostrophes as "'" between
single-quoted runs. Both families read that identically, unlike the sh '\''
idiom, which fish silently halves and which makes a trailing backslash a
hard syntax error.
- clearEnvCommand emits a self-contained fish/sh branch. It deliberately does
NOT call a helper defined by Orca's shell wrappers: Orca wraps only zsh, bash
and fish, so an `sh`/`dash`/`ksh` login shell launches unwrapped — and the
same text is copied to the clipboard and pasted into shells Orca never
spawned. In both, a helper would be `command not found`, which is the exact
failure this exists to avoid. Two guarded statements rather than `A && B ||
C`, because fish's `set -e` returns non-zero for an already-unset variable
and would fall through to the sh branch; a trailing `true` pins the status,
since this is the last statement of a launch line and the prompt renders it.
- One tokenizer for Unix. The input is a settings string the shell never
parses, so parsing it per-shell only made the same setting mean different
things in different workspaces.
AgentStartupShell loses its 'fish' and 'unix' members, and the three
login-shell resolvers, the fish tokenizer and the agentEnv.SHELL probe go with
them.
Per-worktree shell history now actually works:
- zsh on macOS was a no-op. /etc/zshrc assigns HISTFILE unconditionally before
any wrapper Orca controls, so the injected value was already gone — and with
ZDOTDIR still pointing at Orca's wrapper dir, history landed inside it. The
intended path rides ORCA_HISTFILE and is restored after user config.
Fixes#11044.
- fish keeps history in its own data dir keyed by session name, since it
ignores HISTFILE and has no custom-directory knob. Files are deleted rather
than truncated, a symlinked ~/.local/share no longer disables cleanup, and a
GC sweep reclaims orphans whose meta.json is gone. The sweep refuses an empty
live-worktree set (indistinguishable from a store that failed to hydrate) and
skips files younger than GC_MIN_AGE_MS, mirroring the tree GC's guard against
the live-set snapshot race.
Verified against real shells rather than asserted as strings:
startup-shell-portability.live-shell.test.ts runs 194 assertions across
sh/bash/zsh/dash/ksh/fish, and zsh-scoped-histfile.live-shell.test.ts drives a
real login zsh through /etc/zshrc. Both are vacuity-checked. The same quoting
corpus was replayed byte-exact on Linux, where /bin/sh is dash.
* fix(terminal): preserve Option-composed ASCII input
* fix(terminal): preserve Option keyboard protocol semantics
* fix(terminal): complete Option keyboard event encoding
* fix(terminal): harden Option input encoding
* fix(terminal): close keyboard protocol fallback gaps
* test(terminal): prove Option-composed ASCII reaches the pty end to end
The Option-compose fix had unit coverage only. This drives a live Electron
pane whose kitty flags are armed by the application's own CSI > 1 u and
asserts the bytes at the pty boundary: composed `@` and Shift-layer `\`
arrive as text, configured Option-as-Alt still reports the layout-resolved
chord, and a non-ASCII glyph still reaches the app as its alt hotkey.
Restoring the pre-fix policy fails exactly the two composed-text scenarios.
Also records the ASCII rule's rationale where the rule lives, not only in a
test comment.
* refactor(terminal): drop the unread Option layers from the layout snapshot
The native helper computed an Option and Option+Shift character for every
key, shipped both over IPC, validated them in the parser and cached them in
the renderer — but no production caller ever asked for them. Only the base
and Shift layers are read, and Shift is the one the web layout map cannot
supply, which is why the helper exists at all.
Removing them halves the helper's UCKeyTranslate work per key and drops the
option parameter that six signatures were threading through for nobody.
* ci: color added/deleted LoC counts in PR summary
* ci: use GitHub color-swatch dots for added/deleted LoC counts
* ci: color LoC counts with LaTeX textsf
* ci: bold LoC counts; render zero in white
* ci: render LoC counts large bold sans-serif
* ci: use bold math font for LoC counts
* ci: color only the + and - signs on LoC counts
* refactor: split editor.ts under 400 lines
Move editor slice types, file-id/tab helpers, and action factories under
src/renderer/src/store/slices/editor/. The source file is now a public
barrel. No intentional behavior change.
* refactor: split editor-chrome-slice into state modules
- Move EditorDraftState, ExplorerDirState, and RightSidebarState type definitions into their respective action files
- Simplify state creator return types from Pick<EditorSlice, ...> to specific state types
- Improve modularity by colocating types with implementations
* refactor(persistence): extract modules to half persistence.ts
* refactor(persistence): tighten the extracted operations seam
Review follow-ups on the module extraction, all behavior-neutral.
The extracted operations read and mutate the Store's state object in place, but
every seam typed it as a bare PersistedState, so nothing at the boundary said a
caller must pass the live reference — a future caller handing over a clone would
have its writes silently dropped. Name that contract: StoreOwnedPersistedState
carries it to every operations interface and every mutating free function.
normalizePersistedPaneIdentityState and backfillFolderScopeConnectionIds stay on
PersistedState; they build a fresh state rather than mutating the Store's.
The six *PersistenceOperations wrappers were constructed per delegate call. They
are stateless today, so this was inert, but any future instance state would be
lost between calls. Memoize them, and mark state and gitUsernameCache readonly
so the compiler enforces the single-assignment invariant memoizing them relies
on.
Also: restore flushSshPtyConsumerRecovery, whose inlining left its rationale
duplicated at both call sites; document that migrateWorktreeIdentity's boolean
gates the caller's save, since the extracted function kept no docs of its own;
and merge a duplicate shared/types import that was failing lint under
--deny-warnings.
* delete plan doc
* refactor(persistence): add error recovery and improve field cleanup
- Rollback failed migrations to prevent corrupted state that blocks retry
- Gracefully skip malformed entries in normalization instead of aborting
- Strip retired fields to prevent orphaned state and sync issues
* refactor(persistence): drop the redundant persistence- filename prefix
The extracted modules already live in src/main/persistence/, so name
them after the domain they own. Point leftover shared/types imports
at the real type modules while touching those files.
* refactor(persistence): optimize lookups and fix unsanitized updates
- Use Maps instead of repeated array searches for O(1) lookups
- Apply sanitized updates instead of raw input in ui-state-update
- Compare fields directly rather than JSON strings to avoid false dirty states from persisted key ordering differences
* refactor(persistence): group modules into lifecycle folders
Move the 42 flat persistence modules into six folders named for what the
module does, and lift the Store class out of the barrel so persistence.ts
becomes an 8-line public surface.
Bodies are unchanged: every moved file diffs clean against HEAD once import
blocks are excluded. Only import specifiers were rewritten, by resolving each
one to an absolute path and mapping it through the move map.
Store keeps its existing max-lines suppression; its baseline entry is repathed
rather than re-added. Its 119-method public API sets a ~525-line floor, so it
cannot meet the 400-line cap without breaking the API for 153 importers.
* Sanitize worktree visibility sources and preferences on hydration
Ensure invalid or corrupted data from disk (untracked whitespace,
relative paths, bogus preference values) is cleaned during load
rather than corrupting the in-memory store.
`source-control-dropdown-items.ts` was 524 counted lines behind an
`eslint-disable max-lines`. It splits along the seams the resolver already had:
- `source-control-dropdown-item-types` — the row union, consumed by CommitArea,
the composer and the action dispatcher without pulling in the state machine.
- `source-control-dropdown-labels` — count/label/title wording.
- `source-control-dropdown-action-context` — the branch, upstream and review
facts every row reads, derived once so rows cannot disagree about them.
- `source-control-dropdown-commit-items` / `-remote-items` / `-review-items` —
the three row groups, each keeping its own disabled-reason ladder intact.
`resolveDropdownItems` is now just the entry order plus the conflict-abort and
hosted-review-busy passes.
Verified output-identical to the pre-split resolver: a differential harness ran
both implementations over 16,380 generated input combinations (every upstream
shape × PR state × conflict operation × blocked reason × staged count ×
provider) and compared entries deeply. The harness was scaffolding and is not
committed.
Splits the nine oversized modules in the daemon/provider/runtime domain into
focused per-concern files and drops their max-lines baseline entries.
- daemon: `Session` decomposes into an output plane (emulator, pending-output
buffer, client fan-out), a producer-pause controller, a shell-ready barrier and
a termination controller; `DaemonClient` into socket connect, hello handshake,
ndjson readers, pending-request settlement, listener registry and notify
settlement; `daemon-health` into pid-file parsing, process identity,
stale-kill, TCC attribution and bundle staleness; `shell-ready` into the marker
constant and the bash/zsh rcfile generators.
- providers: local-pty shell-ready wrapper generation, wrapper root, startup
command and bash rcfile split out of local-pty-shell-ready.
- runtime: `Coordinator` sheds DAG convergence, decision gates, escalation
triage, the runtime contract, the stale-base flag and task dispatch; the
files/git/github rpc modules split into per-domain method groups.
Behavior-preserving: the extracted units keep their original construction order,
guards and timer lifetimes, and every RPC method name is still registered.
Test `vi.mock` surfaces were re-partitioned to follow the moved symbols.
The two tab-group hooks, the pane manager, worktree activation, and the terminal
pane context menu each carried a file-level `eslint-disable max-lines` and ran
461-745 counted lines against a 300-line budget. AGENTS.md calls for splitting
rather than suppressing, and config/max-lines-baseline.txt is a shrink-only
ratchet, so this removes all five suppressions and prunes their entries
(341 -> 335).
Pure move, no behavior change. useTabDragSplit is cut into gesture lifecycle,
hover preview and drop commit; useTabGroupWorkspaceModel into item projections
plus the tab-close, close-scope, activation and creation command sets; the pane
manager into host, tree mutations, pane creation, drag wiring, reparent frame
tracking, layout sweeps and rendering diagnostics.
react-hooks exhaustive-deps stays at zero warnings, matching HEAD. Dependency
additions are only stable identifiers -- refs and callbacks that became
parameters -- and no `.current` dereference was added to any dependency array.
Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green
(the three remaining failures are pre-existing load flakes in untouched files,
each green when re-run serially), no new runtime import cycles among 1020
modules, no barrel files, and no lint suppression added anywhere.
The four editor modules, the diff-comment decorator and the file-type icon table
each carried a file-level `eslint-disable max-lines` and ran 319-806 counted
lines against 300/400-line budgets. AGENTS.md calls for splitting rather than
suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so this
removes all six suppressions and prunes their entries (341 -> 334).
Pure move, no behavior change. MonacoEditor is cut along its own seams -- mount,
input bindings, markdown annotations, decorations, content sync, view-state
persistence and reveal scheduling -- with the markdown overlay becoming its own
component. useEditorPanelContentState splits into file and diff content loaders
plus the active-tab load and reload triggers.
When the mount module came in at 370 counted lines, over the 300 ceiling, it was
split again into its parameter types and its input bindings rather than carrying
a suppression.
Hook usage is identical to HEAD across all three React split families: the same
counts of every hook type between each original and its extracted modules, so no
hook was added, dropped, or converted to a plain function. react-hooks
exhaustive-deps stays at zero warnings, matching HEAD.
Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green
(the four remaining failures are pre-existing load flakes in untouched files,
each green when re-run serially), no new runtime import cycles among 884
modules, and no lint suppression added anywhere.
The six right-sidebar modules and the remote file browser each carried a
file-level `eslint-disable max-lines` and ran 347-797 counted lines against
300/400-line budgets. AGENTS.md calls for splitting rather than suppressing, and
config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all seven
suppressions and prunes their entries (341 -> 333).
Pure move, no behavior change.
Two renderer-specific hazards were found and fixed rather than shipped.
First, effect and ref LIFETIME. FileExplorer's `if (!worktreePath) return` sits
above the files pane, so moving the worktree-reset effect into that pane made its
guard ref `lastResetWorktreePathRef` die on any render where worktreePath was
transiently null (workspace-list refresh, store rehydrate, remote worktree
reload). On remount the guard read null, so the reset fired even when returning
to the SAME worktree -- wiping dirCache, collapsing every expanded directory,
clearing the name filter and undo history, and forcing a full re-read over SSH.
The tree-load effects now live in a hook called from FileExplorer above the early
return, and the pane is purely presentational with zero hooks. That also restores
the original parent-effect ordering, which had shifted because React flushes
child effects before parent effects.
Second, extracting a hook silently degrades dependency analysis: `setX` setters
that the linter knew were stable when created locally become opaque parameters,
producing 8 new react-hooks/exhaustive-deps warnings where src/renderer had zero.
Those are fixed by listing the genuinely stable identifiers (useState setters and
ref OBJECTS). No `.current` dereference was added to any dependency array, since
that would change callback identity as the ref mutates.
Verified: oxlint clean with exhaustive-deps back to zero, ratchet passes,
typecheck clean, full unit suite green on the first pass, no new runtime import
cycles, no lint suppression added, and hook usage identical to HEAD across both
split families.
The three usage scanners and their stores, plus the renderer usage-overview
model, each carried a file-level `eslint-disable max-lines` and had grown to
338-769 counted lines against a 300-line budget. AGENTS.md calls for splitting
rather than suppressing, and config/max-lines-baseline.txt is a shrink-only
ratchet, so this removes all seven suppressions and prunes their entries
(341 -> 334).
Each file is cut along the seams it already had -- and that several of the
suppression comments named out loud: filesystem discovery / record parsing /
attribution / aggregation for the scanners, and pricing policy / scope filters /
rollups / session rows / automation attribution for the stores.
Pure move, no behavior change. Code is relocated verbatim; the only edits are
import plumbing and, where a private class method became a free function, the
mechanical `this.state` -> `state` parameter threading. Every converted call
site passes `this.state` at call time and the automation path takes a live
`getState: () => this.state` getter, so no state is snapshotted. No barrel
exports: each new module owns real logic and importers point at the owner.
Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green
(remaining failures are pre-existing load flakes in untouched files, each green
when re-run serially), no import cycles among the 64 affected modules, and a
statement-level diff of every split confirms the moves are verbatim.
The four agent hook services, the main hooks module, and the two relay modules
each carried a file-level `eslint-disable max-lines` and ran 365-628 counted
lines against a 300-line budget. AGENTS.md calls for splitting rather than
suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so this
removes all seven suppressions and prunes their entries (341 -> 334).
Pure move, no behavior change. Each hook service splits into its managed script
source, its config/bundle serialization, and its remote-install path, keeping the
per-agent integrations independent: copilot, amp, antigravity and hermes each
retain their own getManagedScript rather than sharing one, because each emits a
different script body for a different agent. Merging them by name would have
been a behavior change, not a refactor.
For antigravity the suppression's stated rationale -- that local install, Windows
wrapper generation, status cleanup, and SSH remote install must share one event
list and managed-command matcher so stale-hook cleanup cannot drift by platform
-- is now enforced structurally instead: both install paths call
buildInstalledConfig + createAntigravityManagedCommandMatcher over the single
ANTIGRAVITY_EVENTS catalog, with the graph a strict DAG.
Also registers the six new antigravity/ and copilot/ modules in
config/tsconfig.cli.json. That project uses a curated `include` list rather than
a glob, so an unlisted module fails `tsc -p config/tsconfig.tc.cli.json` with
TS6307 even though the entire unit suite passes.
Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green
(remaining failures are pre-existing load flakes in untouched files, green when
re-run serially), no new runtime import cycles, and no lint suppression added.
The GitLab, GitHub, Jira and Linear integration modules, their two IPC
registrars, and the shared GitHub project types each carried a file-level
`eslint-disable max-lines` and ran 351-614 counted lines against a 300-line
budget. AGENTS.md calls for splitting rather than suppressing, and
config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all
eight suppressions and prunes their entries (341 -> 333).
Pure move, no behavior change. Each client is cut along the seam it already
had: per-operation modules for the issue APIs (create / update / comment /
field options), and for Jira the request queue, site credential store,
authenticated request, and site identity. The two IPC registrars keep their own
handlers and delegate the rest to per-domain sub-registrars, so they remain
real entry points rather than re-export shims.
The IPC surface is proved intact rather than assumed: comparing (method,
channel) multisets between HEAD and the split gives 52 registrations across 52
distinct channels on both sides.
Provider-neutrality is preserved -- GitLab and GitHub keep separate, parallel
module layouts rather than being merged behind a shared abstraction.
Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green
(the one remaining failure is a pre-existing load flake in an untouched file,
green when re-run serially), no new runtime import cycles among 744 modules,
and no lint suppression added anywhere.
The six oversized src/main/ipc modules each carried a file-level
`eslint-disable max-lines` and ran 427-671 counted lines against a 300-line
budget. AGENTS.md calls for splitting rather than suppressing, and
config/max-lines-baseline.txt is a shrink-only ratchet, so this removes all six
suppressions and prunes their entries (341 -> 335).
Pure move, no behavior change. Each file is cut along the seams it already had:
pet splits into format allowlist / storage paths / symlink-safe copy / bundle
manifest + import; filesystem-auth into path-containment primitives, the
config-derived allow-list, and the git-registered root cache; notifications into
sound selection, native lifecycle, permission probe, and burst cooldown;
crash-reporting into renderer error reports, breadcrumbs, and sender.
The IPC surface is proved intact rather than assumed: comparing (method,
channel) multisets between HEAD and the split gives 49 registrations across 49
distinct channels on both sides. filesystem-auth's security boundary keeps its
acyclic layering -- containment primitives, then allow-list, then root cache,
then path-resolution orchestration -- with no layer gaining a back-edge.
Also keeps clipboard-ipc-handlers.test.ts under the 800-line test budget. The
split had briefly added a redundant vi.mock for isENOENT (byte-identical to the
real implementation) that pushed it to 801; the mock is dropped in favor of the
real function, with realpath added to the existing node:fs/promises mock.
Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green
(the one remaining failure is a pre-existing load flake in an untouched file,
green when re-run serially), no new runtime import cycles among 617 modules,
and no lint suppression added anywhere.
The five oversized src/main/browser modules and src/main/ipc/browser.ts each
carried a file-level `eslint-disable max-lines` and ran 377-654 counted lines
against a 300-line budget. AGENTS.md calls for splitting rather than
suppressing, and config/max-lines-baseline.txt is a shrink-only ratchet, so
this removes all six suppressions and prunes their entries (341 -> 335).
Pure move, no behavior change. cdp-ws-proxy is decomposed into collaborating
objects rather than free functions because its state is genuinely
per-connection: every collaborator is a private readonly instance field built
in the constructor with live closures over `this`, so per-connection state
stays per-connection. Likewise the screencast pacer's isClosed/isStopping and
snapshot capture's getSeq are live thunks, not values captured at wiring time,
so guards inside already-armed timers still observe a later stop().
browser-guest-ui.ts is renamed to browser-guest-shortcut-forwarding.ts: after
the split it exports exactly one function, setupGuestShortcutForwarding, so the
old name no longer described its contents.
Also restores a single `webContents.debugger` read in the screencast path. The
extraction had left three reads where the original had one; the accessor is
stable today, so this is not a behavior fix but it removes a latent divergence.
Verified: oxlint clean, ratchet passes, typecheck clean, full unit suite green
(remaining failures are pre-existing load flakes in untouched files, each green
when re-run serially), no new runtime import cycles, and the IPC channel set
diffed identical before/after with all 23 handlers still trust-gated.
* docs(source-control): plan half-size extraction
* refactor(source-control): extract modules to half SourceControl.tsx
* move git decoration token comment to correct component
* delete plan doc
* test: add useSourceControlBranchCompare and git-history hook tests
Comprehensive unit tests covering the scheduling, stale response filtering,
and visibility logic of the extracted branch-compare and git-history hooks.
* refactor(source-control): internationalize UI strings
Add translate() support for all hardcoded strings throughout source control UI,
extract reusable SourceControlTreeDirectoryHeader component, improve error
handling in bulk operations with logging and user-facing toasts, and add proper
return type annotations to hooks.
* fix(source-control): satisfy react-doctor rules in extracted modules
Reset worktree-scoped hook state during render instead of in an effect,
and give dropdown separators stable ids so the changed-code quality gate
stops flagging the extracted SourceControl modules.
* docs: add JSDoc comments to source-control hooks and components
Clarify the purpose, behavior, and constraints of test-harness functions,
directory-row components, and the git-history hook to help maintainers
understand the extracted and refactored source-control module.
* refactor: organize source-control into lifecycle dest folders
* fix(source-control): clear remaining react-doctor findings
Reset worktree- and history-scoped state during render, keep Cmd/Ctrl
selection updates free of setter side effects, and key graph paths by
swimlane/parent id. Also add the PR LoC helper scripts the quality
workflow fetches from the branch head.
* fix(source-control): refetch git history when owner host changes
Track activeRuntimeEnvironmentId as a stable key in useSourceControlGitHistory so that when the owner host changes but the worktree and path remain the same, the git history panel correctly refetches from the new host instead of keeping stale commits from the previous one. Add ownerHostKey to the useEffect dependency array to trigger refetch on host changes. Include JSDoc documentation for related components and expand test coverage to verify the host-change scenario.
* fix(source-control): refetch git history when owner host changes
Track activeRuntimeEnvironmentId as a stable key in useSourceControlGitHistory so that when the owner host changes but the worktree and path remain the same, the git history panel correctly refetches from the new host instead of keeping stale commits from the previous one. Add ownerHostKey to the useEffect dependency array to trigger refetch on host changes. Include JSDoc documentation for related components and expand test coverage to verify the host-change scenario.
* refactor(source-control): consolidate bulk mutation error handling
Extracts repeated error reporting into a dedicated helper function and applies
it consistently across all bulk stage/unstage handlers, including two that were
previously missing error handling.
* refactor(sidebar): group worktree-list files by domain
Follow-up to #14465 / #14467. Keep the landed extract and reorganize the
flat worktree-list dump into drag/, headers/, reveal/, rows/, scroll/,
and viewport/. Fold tiny modules into their owners, move leftover
sidebar-root files into the module, and retarget imports and source-path
tests. Layout-only; no behavior change.
* fix(sidebar): merge duplicate virtual-rows imports
Inlining virtual-row-dom-attributes left a second import from the same
module, which fails audit:code-quality:native --deny-warnings.
* refactor(sidebar): condense indentation comments
Shorten explanations to focus on the essential why, removing redundant
detail and improving readability without changing functionality.
* refactor: organize worktree-list into lifecycle dest folders
* fix react doctor
* fix: update reliability-gates path after worktree-list reorg
host-filtering.test.ts moved from viewport/ to listing/; keep the
runtime-routing.active-server-preference gate pointing at the real file.
* Extract workspace status colors to design tokens
Define theme-aware color tokens for workspace PR-state indicators (done, in-review, in-progress) to ensure consistent identity across theme switches. Update references to use the new tokens and refactor EmptyState button to use the Button component.
* fix(sidebar): stop mutating refs during worktree-list render
React Doctor fails static analysis when refs are written in render.
Commit reused array identity and the Smart live-signal latch after
paint, and return the attention map from the sort memo instead of
stashing it on a render-time ref.
#14738 landed a template literal interpolating an untyped fetch url, which
fails audit:code-quality:type-aware. That audit runs in the static analysis
job, so main is currently red and every open PR inherits the failure.
* refactor(tests): split oversized test files off the max-lines suppression list
Every `*.test.ts`/`*.spec.ts` that carried an `eslint/oxlint-disable max-lines`
directive is now split into focused, behavior-scoped suites that fit the 800-line
test budget, with shared setup extracted into co-located `*-test-harness.ts` /
`*-test-fixtures.ts` modules (300-line budget). 83 files became ~930; the largest
output is 797 effective lines. `orca-runtime.test.ts` is intentionally untouched.
Test bodies were moved by scripted line-range slicing rather than retyped, so
assertions are byte-identical. The only permitted body edits were mechanical
rebinding where a shared value moved into a harness (e.g. `tmpHome` ->
`homes.tmpHome`).
Registries that enumerate test files were updated in lockstep:
- config/max-lines-baseline.txt: pruned 341 -> 258 entries (all 83 removed).
- config/reliability-gates.jsonc: 33 gates repointed at the split files, with
assertionRefs split per file where a gate's coverage now spans several.
- .github/workflows/pr.yml: the real-zsh lane now lists the 4 split files that
actually exercise zsh, so they keep running in the dedicated shell lane.
Also renamed agent-hooks `server-test-fixtures.ts` to `server.test-fixtures.ts`
so the global-fetch call-site audit keeps skipping it, and added `.js` extensions
to the CLI suites' dynamic harness imports (node16 resolution) to unbreak
`build:cli`.
Verification: full suite 52,449 passing vs 52,448 at baseline with zero
assertions lost; `pnpm lint`, `pnpm typecheck`, and `pnpm build:cli` all exit 0;
the terminal-pane e2e spec runs 31/31 headless.
* refactor(tests): split hook-idle arbitration suite that oxfmt pushed over budget
The pre-commit oxfmt pass reflowed pty-connection-hook-idle-arbitration.test.ts
to 811 effective lines, 11 over the test budget. Split the hook-completion side
effect and replacement-agent veto cases into their own suite; both files now sit
well under the cap and the 15 tests are unchanged.
* test: port upstream test changes into the split files after rebase
Rebasing onto main surfaced 27 tests that main had added to files this branch
deleted, plus edits to tests that had already moved. Taking the deletion side of
those modify/delete conflicts would have dropped that coverage silently, so each
upstream change is ported into the split file that now owns the behavior — for
example main's six orchestration mailbox tests land across orchestration-runs,
-send, and -check.
Also repoints `orchestration.notification-mailbox-consistency`, a gate main added
after this branch's gate remap, at those same three split files, and re-prunes
the max-lines baseline against main's (257 entries).
Verified: all 27 upstream test titles present; full suite 52,761 passing with the
only diff vs baseline being 12 tests main itself removed and 3 that moved from
skipped to passing; lint and typecheck exit 0.
* fix(test): flush pending continuations before tearing down terminal test globals
CI shard 5/16 failed on both Node 24 and 26 with `ReferenceError: window is not
defined` from pty-connection.ts, surfacing through
pty-connection-daemon-snapshot-replay.test.ts.
The reattach/settle chains `await` a real promise and then touch `window.api`.
Under fake timers those continuations cannot run, so they only become schedulable
once restoreTerminalTestGlobals() switches back to real timers — which previously
happened immediately before `delete globalThis.window`, so a late continuation
threw and failed the whole file. Flush async ticks in that window instead.
This is latent in the source rather than new: the pre-split 25k-line file kept
running other tests after these, which gave the chains time to settle before
teardown. Splitting the file moved teardown directly behind them.
* fix(test): keep an inert window after terminal test teardown instead of deleting it
The async-tick flush was not enough: the reattach/settle chain can resolve after
teardown regardless of how long we drain, so CI shard 5/16 still failed with
`ReferenceError: window is not defined` from pty-connection.ts.
A real renderer never loses `window`, so deleting it was the artificial part.
Swap in an inert proxy whose properties resolve to callables and whose calls
resolve to undefined, making a late `window.api.pty.*` call a harmless no-op.
The next test replaces it wholesale via installTerminalTestGlobals(), and no test
asserts that `window` is absent.
* feat(computer-use): support macOS middle click and gate the AX click path
`--mouse-button middle` already validated end-to-end through the CLI, the
zod schema, and the provider validator, and both the Windows and Linux
providers honored it. Only the macOS provider rejected it outright with
"middle-click is not yet supported", so the flag was a dead end on the one
platform that has no fallback.
Two changes:
- Add `.middle` to the macOS button mapping. macOS has no dedicated middle
event family, so it rides `otherMouseDown`/`otherMouseUp` with the button
number carried by `mouseButton: .center`; that constructor argument is
honored for exactly the `otherMouse*` types, so no extra field write is
needed.
- Validate the requested button before the accessibility fast path, and skip
that path for buttons it cannot express. Previously the raw string was read
unvalidated, and `performClickAction` only special-cased `right`, so
`click --mouse-button middle --element-index N` (no modifiers, count 1) fell
through to `AXPress` — a left click — and reported success with
`path: "accessibility"`. Any unrecognized button string did the same. This
matches guards the Windows and Linux providers already had.
The button enum moves into `OrcaComputerUseMacOSCore` so it is unit-testable;
`main.swift` keeps only the CoreGraphics mapping.
Also documents `--mouse-button` in the computer-use skill guide, which never
mentioned the flag, so agents on Windows and Linux had no way to discover it.
* test(computer-use): cover macOS middle click in the real-desktop e2e suite
* test(computer-use): prove macOS middle-click delivery
* refactor: split worktrees.ts under 400 lines
Move the worktree slice into dest modules under store/slices/worktrees/ and leave a thin public barrel so existing imports keep working.
* refactor: nest worktrees dest modules by domain
Group the extracted slice files into catalog, refresh, create, remove, and related domain folders instead of a flat dest dump.
* refactor: group worktrees dest modules by lifecycle
Collapse the 12 noun folders into listing, create, teardown, metadata, and session so dest layout matches the slice methods.
* rm duplicated mport
* refactor(renderer): decompose App.tsx into an app-shell module
App.tsx was 2831 lines behind an `eslint-disable max-lines` and a
grandfathered entry in the max-lines ratchet baseline. It is now 92 lines:
a root element, the shared providers, and three children.
The body is split by concern into src/renderer/src/app-shell/:
- use-app-chrome-layout — titlebar/sidebar/workbench layout derivations
- use-floating-workspace-panel — overlay open state, persistence, return focus
- use-app-startup-hydration — the boot chain (order preserved verbatim)
- use-app-session-persistence — session writer + shutdown checkpoint
- use-persisted-ui-writer / use-document-appearance /
use-runtime-graph-sync / use-window-visibility-effects
- use-onboarding-and-feature-tips — first-run education gating
- use-global-keybindings + app-command-handlers — window shortcut dispatch
- use-app-shell-services — app-level subscriptions that outlive any surface
- AppWorkspaceShell / AppRootSurfaces / AppBackgroundServices / titlebar parts
Two startup branches that no longer needed to sit inline moved to
src/renderer/src/startup/: startup-ssh-connection-restore and
startup-degraded-recovery.
No behavior change. Sibling order of root overlays and modals is preserved
so stacking is unchanged; the sidebar's virtualized scroll refs still live
above the sidebar's remount boundary.
The source-assertion tests in app-startup-routing.test.ts follow the code to
its new files. The one that claimed to check "first-window startup services
before terminal reconnect" was matching the degraded-recovery block, not the
success path; it now asserts the success path, and the degraded ordering
keeps its own dedicated test.
Removes the disable comment and drops App.tsx from
config/max-lines-baseline.txt.
* refactor(renderer): move app-shell ref writes out of render
React Doctor's changed-lines purity gate flags three render-phase ref
writes in the new app-shell files. The patterns predate the split, but
moving them into new files brings them into the gate.
- use-app-chrome-layout: the terminal-workbench latch becomes state set
during render (the pattern use-lazy-modal-mounts already uses). The
`canMountTerminalWorkbenchNow ||` term keeps the current render correct,
so the latch only has to be visible to the next one.
- use-app-startup-hydration: the onboarding callback ref syncs in an effect
declared before the boot chain, so it lands first on mount. The callback
is a `useCallback([])`, so its identity never actually changes.
- use-global-keybindings: the shortcut-state mirror syncs in useLayoutEffect,
which commits before any key event can read it. Key events are discrete,
so handlers always observe committed state.
All three now hold committed state only, so nothing can leak from a render
React discards — the behavior the gate is protecting.
pnpm run check:react-doctor:changed: 0 errors (was 3).