Commit Graph
37 Commits
Author SHA1 Message Date
Jinjing 7705415fc2 refactor(github-client): reorganize client into lifecycle folders 2026-08-17 21:18:27 -07:00
Jinjing a3a2c44edf Split browser pane (#14861)
* refactor: split BrowserPane.tsx under 400 lines

* rm plan

* refactor(browser-pane): reorganize into lifecycle folders

Cut/paste + import rewrites only; no intentional behavior change.

- annotate/, assemble-chrome/, host-guest/, navigate/, stream-remote/,
  describe-page/ (foundation sink, zero outgoing edges)
- BrowserPane.tsx is now a pure re-export barrel; its component body moved
  verbatim to assemble-chrome/browser-workspace-pane.tsx so no dest file
  imports the barrel
- browser-runtime.ts -> describe-page/live-browser-url-registry.ts (banned
  name; relocating the contract collapsed the host-guest/navigate mutual pair)
- repath browser-pane test paths in config/reliability-gates.jsonc

* refactor: sync addressBarValueRef with useEffect

Move ref synchronization into useEffect hook with proper dependency
tracking to ensure the ref updates are handled through React's
lifecycle. Consolidate related imports from browser-page-types.

* refactor(browser-pane): fix React lifecycle and external store patterns

- Replace local state + effects with useSyncExternalStore for external subscriptions (draw hint, address bar, slot viewport)
- Fix React StrictMode double-invoke issues in pointer handlers and state updates
- Add keyboard navigation to context menu (arrows, Home, End, Escape) with focus management
- Improve error handling for mobile driver reclaim and grab action IPC failures
- Add test coverage for BrowserFind session flags, keyboard behavior, viewport lifecycle
- Remove react-doctor/no-adjust-state-on-prop-change lint disables (root causes now fixed)

* i18n: extract grab and download UI messages

Move hardcoded toast notifications and error messages to translation
system for both grab annotations and file drop handling. Also apply
lazy initialization to address bar value and remove duplicate event
recording.

* fix(browser-pane): stop mutating refs during render

React Doctor fails static analysis when refs are written in render.
Mirror latest values in useLayoutEffect, and read the current page id
from the latest grab callbacks.

* fix(browser-pane): drop unused grab-mode exit dependency

exit already reads the page id from a ref, so listing browserPageId
trips the changed-code exhaustive-deps gate.

* test(e2e): hide the window when Linux minimize is a no-op

Xvfb has no window manager, so BrowserWindow.minimize() never sets
isMinimized() on the frameless Linux CI window. Hide still occludes
the guest compositor so restore coverage can run.
2026-08-17 14:53:19 -07:00
Jinjing 81d7f9b24e refactor: split db.ts under 400 lines (#14979)
* refactor: split db.ts under 400 lines

* rm plan

* fix(orchestration-db): add safety guards to database operations

Add status guards to UPDATE statements to prevent late operations from
overwriting changes made by concurrent requests. Validate mutation results
to surface silent no-ops. Extract circuit-break threshold, add transaction
wrapping, and sanitize untrusted input. Bump schema version to v28.

* Add transactional safety to dispatch and message operations

Wrap dispatch failures and batched message updates with SAVEPOINTs to
ensure atomicity and idempotency:
- Dispatch failures now check status guards and roll back if the
  related task update fails, preventing partial state corruption
- Message batches (across multiple 500-id chunks) roll back entirely
  if any batch fails, avoiding partial mutations
- Add tests verifying idempotency and atomicity under failure conditions

* Handle concurrent writes and improve transactional safety

- Remote question answering: add classification check before and after UPDATE to safely detect concurrent modifications. Prevents false success when the UPDATE loses a race.
- Transaction rollback: wrap in try-catch to prevent errors from masking the original failure.
- Question thread reset: use status update instead of deletion to preserve message references.

* test: add answer replay and race condition edge case coverage

Add test cases for answer replay idempotency, conflict detection, and a race condition between concurrent answer updates in federation relay. Also verify local question state transitions during orchestration reset.
2026-08-16 22:44:43 -07:00
Jinjing 84784f5393 Split pull request page (#14853)
* 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.
2026-08-16 21:45:41 -07:00
Jinjing 9c4627d1c6 Refactor: split GitHubItemDialog into lifecycle-organized modules (#14931)
* 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
2026-08-16 16:44:48 -07:00
Jinjing 31e9f4af30 Refactor: split editor.ts into modular state actions (#14847)
* 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
2026-08-15 22:14:37 -07:00
Jinjing 8b04e060fa refactor(persistence): extract modules to half persistence.ts (#14252)
* 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.
2026-08-15 21:41:01 -07:00
Neil 306c5d545f refactor(source-control): split the dropdown action resolver under the max-lines budget (#14835)
`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.
2026-08-15 19:44:02 -07:00
Neil 73aa5d0ca7 refactor(daemon,runtime): split daemon, pty and rpc modules under the max-lines budget (#14834)
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.
2026-08-15 19:36:03 -07:00
Neil 6854cceb90 refactor(panes,tabs): split pane manager and tab-group modules under the max-lines budget (#14760)
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.
2026-08-15 19:10:36 -07:00
Neil cc19692f93 refactor(editor): split Monaco, autosave and notebook modules under the max-lines budget (#14748)
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.
2026-08-15 19:02:11 -07:00
Neil 98450718f7 refactor(right-sidebar): split file explorer and sidebar under the max-lines budget (#14741)
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.
2026-08-15 18:48:23 -07:00
Neil 97b71c2285 refactor(usage): split AI-usage scanners and stores under the max-lines budget (#14668)
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.
2026-08-15 18:33:33 -07:00
Neil bc28107864 refactor(hooks,relay): split agent hook services and relay under the max-lines budget (#14725)
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.
2026-08-15 18:25:37 -07:00
Neil 83117f2860 refactor(integrations): split issue-tracker clients under the max-lines budget (#14704)
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.
2026-08-15 18:17:20 -07:00
Neil 15e1ba3f84 refactor(ipc): split main-process IPC modules under the max-lines budget (#14703)
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.
2026-08-15 18:08:54 -07:00
Neil c8fe5fc8c1 refactor(browser): split browser and browser-IPC modules under the max-lines budget (#14697)
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.
2026-08-15 17:59:29 -07:00
Jinjing 93ab6e142e refactor(source-control): extract modules to half SourceControl.tsx (#14396)
* 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.
2026-08-15 16:47:03 -07:00
Jinjing 7aaa7c6f5b refactor(sidebar): group worktree-list files by domain (#14486)
* 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.
2026-08-15 13:40:09 -07:00
Neil 9367169888 refactor(tests): split every oversized test file off the max-lines suppression list (#14728)
* 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.
2026-08-15 00:54:20 -07:00
Jinjing 8b31910ac1 Split worktrees slice (#14643)
* 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
2026-08-14 18:44:44 -07:00
Neil a3b472d050 refactor(renderer): decompose App.tsx into an app-shell module (#14607)
* 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).
2026-08-14 17:46:44 -07:00
Brennan Benson 9e5ee5ef8e feat(workspaces): rework cleanup discovery and dialog (#13413)
* fix(workspaces): support full cleanup scans

* feat(workspaces): persist cleanup snapshots

* feat(workspaces): add cleanup filter model

* refactor(workspaces): remove cleanup presets

* feat(workspaces): rework cleanup dialog

* fix(workspaces): keep cleanup row ordering render-pure

* refactor(workspaces): simplify cleanup browsing

* refactor(workspaces): show cleanup facts

* refactor(workspaces): surface cleanup row facts

* fix(workspaces): remove misleading cleanup count

* fix(workspaces): preserve full scan semantics

* fix(workspaces): scope snapshot persistence

* fix(workspaces): preserve cleanup browse compatibility

* fix(workspaces): reconcile cleanup dialog state

* test(workspaces): update snapshot store fixtures

* test(workspaces): preserve cleanup scan modes

* perf(workspace-cleanup): stream scan progress and size results

* fix(workspace-cleanup): address review feedback

* fix(workspace-cleanup): preserve host-scoped cleanup metadata

* fix(workspace-cleanup): declare review source dependencies

* fix(workspace-cleanup): align size scan banner

* fix(workspace-cleanup): shorten scan action

* perf(workspace-cleanup): avoid redundant scan IO

* perf(workspace-cleanup): bound restarted evidence scans

* fix(workspace-cleanup): satisfy scan queue lint

* perf(workspace-cleanup): bound scan and snapshot work

* perf(workspace-cleanup): serialize final enrichment

* test(workspace-cleanup): assert final enrichment drain

* fix(workspace-cleanup): stop progress after renderer teardown

* perf: batch workspace cleanup git evidence scans

* perf(workspace-cleanup): stop redundant snapshot and scan work

* fix(workspace-cleanup): resolve review findings across scan, store, and dialog

Correctness:
- Chunk git-evidence dispatches at the shared 500-target limit and exclude
  queued/in-flight ids from target selection, so fleets past the limit can no
  longer strand rows permanently mislabeled as checked-but-unknown.
- Key destructive selection pruning on the user's filter state instead of the
  per-tick matched-set identity; streaming reclassification no longer silently
  deselects rows.
- Clamp the facet clock to max(scannedAt, open time): a stale hydrated
  snapshot no longer misbuckets idle thresholds or keeps dead agents fresh;
  row labels use the same clock.
- Supersede and cancel the previous broad scan when a new one starts (renderer
  registry and same-sender guard in main) instead of racing two fleet scans.
- Gate snapshot persistence on hasTargetedWorkspaceCleanupScan so
  worktreeIds: [] can never persist an empty fleet snapshot.
- Re-apply dismissals at set-time in progress application so a dismissal
  landing mid-enrichment is not clobbered.
- Record a one-off local snapshot prune for single (unbatched) remote deletes
  so removed workspaces cannot resurrect from cache.
- Strip .exe when normalizing foreground process names so Windows agent
  processes match.

Performance:
- Cache per-candidate facet and review-info objects on candidate identity;
  no-op streaming ticks reuse the previous rows array and skip every
  downstream pass; matched-set identity is stable under equal membership.
- Compute facet counts/options only while the filter popover is open.
- Equality-bail git-evidence publishes; structural (non-stringify) facet-group
  comparison memoized in the toolbar.
- Identity-token fast path for the enrichment cache (cache hits skip both
  JSON.stringify signatures); prune viewed/dismissal records on removal and
  expiry; bound the superseded-scan-id set.
- Restore the no-op bail in removeWorkspaceSpaceWorktrees (regression).
- Abort main-side scans when the renderer is destroyed; module-scope
  controller maps survive handler re-registration.
- Batch removal preflight into one targeted scan (with refreshActivity) per
  500 ids instead of one scan per row.
- Scan repos at concurrency 2, report discovered counts upfront for honest
  progress, share fs-activity probes per path (folder workspaces), read only
  the reflog tail, and skip the snapshot read-before-write via a remembered
  scannedAt.

Split workspace-cleanup-worktree-listing, workspace-cleanup-facet-row-caches,
and workspace-cleanup-selection-model out of files that crossed max-lines.

* fix(workspace-cleanup): address verifier findings

- Fall back to a full reflog read when the newest record exceeds the 8KB
  tail window, so an oversized subject cannot hide recent ref activity.
- Bound the single-removal snapshot prune batch id with a UUID; embedding
  the unbounded worktreeId silently failed main's 128-char validation and
  skipped the prune for long remote ids.
- Key the main-side broad-scan supersession by sender AND scan mode so
  legacy suggestion-only and full-workspace scans stay isolated, matching
  the renderer registry.

* fix(workspace-cleanup): own facet caches with useMemo instead of render-time ref writes

React Doctor (CI changed-lines gate) correctly flagged the three cache refs
written during render. Each per-candidate cache now lives in one memo with
the derived context it is keyed on, so the memo deps are the invalidation
and interior fills stay content-addressed; the matched-set identity
stabilization is dropped since its only consumer reads through a
useEffectEvent and never keys on identity.
2026-08-13 23:47:20 -07:00
Neil 08027d0d23 refactor(sidebar): split WorktreeList into a worktree-list module (#14465)
WorktreeList.tsx was 6.8k lines behind an `eslint-disable max-lines`. Break it
into `sidebar/worktree-list/`: the container keeps store wiring and composition,
the virtualized viewport keeps layout, and the drag, reveal, virtualization,
row-model, and row-render concerns each get their own file. Every file now fits
the oxlint budget, so the suppression and its baseline entry are gone.

Behaviour-preserving. The only deliberate cleanups are duplicate branches folded
into shared helpers (drop-preview state updates, status-hover fallback, the two
identical scroll-to-index reveal branches) and a dead sticky-header-index ref.

Tests that asserted on WorktreeList.tsx source text or imported its named
helpers now point at the module that owns them.
2026-08-13 23:35:41 -07:00
Neil 4221f8d429 refactor(preload): split the preload contract into per-domain api modules (#14403)
`src/preload/api-types.ts` was 3,752 raw lines (3,533 counted, 11.8x the
300-line budget) behind an `eslint-disable max-lines`. Almost all of it was a
single `PreloadApi` object type whose ~83 namespace properties were declared
inline, so any IPC surface change meant editing one 2,600-line type.

Give each namespace a named type in its own module under `src/preload/api/`
(`pty-api.ts`, `filesystem-api.ts`, `github-pull-request-api.ts`, ...) and
recompose `PreloadApi` from those names. `api-types.ts` keeps the `declare
global` Window augmentation and re-exports every moved name, so all 52 import
sites are untouched.

Two shapes needed care to stay type-identical rather than merely compatible:

- Three keys (`gh`, `git`, `ui`) are composed from two modules each. A plain
  intersection is NOT identical to the original flat object literal, so those
  use a `Merged<T>` mapped type; a negative control confirmed that dropping it
  fails the parity assertion.
- Keys whose module groups several namespaces use indexed access
  (`fs: FilesystemApi['fs']`) to preserve exact identity and source order.

`config/tsconfig.web.json` and `tsconfig.tc.web.json` enumerate files by path,
so they need `src/preload/api/**/*` alongside the existing `api-types.ts` seed
or the web projects fail TS6307.

Verified by exact type identity, not assignability: 41 assertions of the form
`Equals<Now.X, Before.X>` against a frozen pre-split snapshot, covering every
exported name, plus a per-key pass over all 83 `PreloadApi` keys. All three
projects typecheck clean with those assertions active.

Verification note: these tsconfigs are `composite: true`, and `tsc --noEmit`
will reuse a stale `.tsbuildinfo` and report clean for a state that genuinely
fails. Every result above was produced after deleting the buildinfo, including
a negative control confirming the gate still fails on deliberate drift.

Drops the `max-lines` bypass and its baseline entry (ratchet 346 -> 345).
2026-08-13 20:52:04 -07:00
Neil 583ab1601b refactor(shared): group worktree, github, and linear modules into folders (#14437)
`src/shared` is a flat directory of ~1,150 entries. The worktree, github, and
linear domains accounted for 71 of them, so finding the module you wanted meant
scanning a wall of same-prefixed filenames.

Move each domain into its own folder and drop the now-redundant prefix:

    src/shared/github-pr-types.ts    -> src/shared/github/pull-request-types.ts
    src/shared/worktree-id.ts        -> src/shared/worktree/id.ts
    src/shared/linear-links.ts       -> src/shared/linear/links.ts

This follows the existing `network/` and `new-workspace/` convention in the
same directory, which also drop the prefix inside the folder.

Whole clusters move, including tests. Foldering only part of a domain would be
worse than flat: a reader would have to check both `github/` and the flat
directory, and `github-auth-types.ts` / `github-project-types.ts` are type
modules that belong with the rest. No files with these prefixes remain flat.

Import specifiers were rewritten by resolving each one to an absolute path and
recomputing it, not by string substitution, so the `@/../../shared/...` alias
forms are handled correctly. 501 specifiers across 298 files.

Two things `tsc` cannot catch, handled explicitly:

- `github-project-types.ts` carries its own `max-lines` bypass, so its baseline
  entry is REPOINTED to the new path rather than pruned. Pruning would drop the
  bypass and then flag the new path as a fresh violation. Ratchet stays at 345.
- `mobile/` is outside `pnpm typecheck` and cannot be typechecked here
  (`mobile/node_modules` is empty). Instead every relative specifier in the repo
  was resolved against the filesystem: 174 unresolved before this change and 174
  after — identical, so nothing broke in mobile either.

The pinned `tests/e2e/.cross-version-checkouts` fixtures are deliberately NOT
rewritten; they are a snapshot of an older release and still reference the old
paths.

Verified: cold `tsc --noEmit` green on node, cli, and web (buildinfo deleted
first — these projects are `composite: true` and reuse stale caches).
2026-08-13 20:44:16 -07:00
Neil 070572bfd7 refactor(shared): split shared/types.ts into per-domain type modules (#14397)
`src/shared/types.ts` was 3,981 raw lines (2,825 counted, 9.4x the 300-line
budget) behind an `eslint-disable max-lines`, and is imported by 2,092 files —
the single widest contract surface in the repo.

Move all 320 top-level declarations into 46 per-domain modules
(`repo-types.ts`, `worktree-types.ts`, `github-pr-types.ts`, ...) and reduce
`types.ts` to an explicit re-export barrel, so the 2,092 import sites are
untouched.

`HostSettingOverrides` moves into the pre-existing `host-setting-overrides.ts`
alongside the accessors that operate on it, which also removes that module's
circular import back into `types.ts`.

Named re-exports only, never `export type *`: with star re-exports a name
exported by two modules is silently dropped, which would surface as a confusing
"has no exported member" at a random call site.

Verified lossless mechanically, not by inspection:
- export parity — the module's resolved export set through the TS checker is
  identical before and after (396 names, no additions, no removals)
- declaration parity — all 320 declarations compare character-identical modulo
  comments and whitespace, so no optionality, union order, or generic
  parameter drifted
- `tsc --noEmit` green on the node, cli, and web projects
- `oxfmt --write` is byte-identical, so the barrel is format-stable

Drops the `max-lines` bypass and its baseline entry (ratchet 346 -> 345).
2026-08-13 20:34:31 -07:00
NeilandOrca c86418eaad rm git shim (#14141)
* rm git shim

Drops the terminal git/gh PATH wrapper and its settings toggle. Renames the no-marker shell-ready launch config after what it does.

Co-authored-by: Orca <help@stably.ai>

* rm git shim: clear stale state from older installs

Deletes the orphaned wrapper dir and scrubs inherited env/PATH, so a daemon that outlives the upgrade cannot keep seeding it. Drops a now-unread spawn option.

Co-authored-by: Orca <help@stably.ai>

* rm git shim: cover the daemon and headless paths

Scrub after the PATH prepends (they re-read process.env on the sparse daemon env) and run the cleanup above the serve branch so remote hosts get it too. Retry a locked removal; match PATH case-insensitively.

Co-authored-by: Orca <help@stably.ai>

* rm git shim: keep the scrub final

Refuse to re-prepend a legacy entry during agent-teams PATH promotion, which runs after the scrub. Cover the removal guard.

Co-authored-by: Orca <help@stably.ai>

---------

Co-authored-by: Orca <help@stably.ai>
2026-08-12 18:53:17 -07:00
Neil 0345d71214 refactor(renderer): split oversized TabBar module (#13739) 2026-08-10 23:43:10 -07:00
Neil fdaafc93dd refactor(renderer): split SettingsFormControls below max-lines limit (#13747) 2026-08-10 23:37:40 -07:00
Neil 6964f75cd0 refactor(renderer): share metadata list request lifecycle (#13524) 2026-08-10 20:40:26 -07:00
Neil 600b0b1c15 refactor(renderer): split WorktreeCard (#13715) 2026-08-10 18:45:11 -07:00
Neil 96c2cebfa6 refactor(mobile): extract terminal settings styles (#13706) 2026-08-10 17:52:45 -07:00
Neil 5e9186f5ff chore(mobile): import the shared marine-creature corpus instead of mirroring it (#12090)
mobile/src/constants/marine-creatures.ts was a hand-maintained copy of
src/shared/marine-creatures.ts, identical except for a comment header. The
copy existed because Metro only watched mobile/ and could not resolve
repo-root modules; mobile/metro.config.js:11 added src/shared to
watchFolders five weeks later, and ~195 mobile files already import from
src/shared. The renderer collapsed its copy to a re-export at the same time;
mobile was the leftover.

Point the one consumer at the shared corpus and delete the mirror, the
bespoke regex-scraping parity test that policed it, and the now-stale
max-lines baseline entry.

No behavior change: same exported symbol, byte-identical name list.
2026-08-02 01:44:03 -07:00
KyuJoo HanandOrcaWin 6f3845baa4 fix(checks): rank successful checks above skipped and neutral (#11337)
* fix(checks): rank successful checks above skipped and neutral

Checks were ordered with `skipped` (4) and `neutral` (3) ahead of
`success` (5), so a PR with a long tail of skipped jobs pushed every
passing check below the fold — you scroll past a wall of "Skipped" to
find out whether anything actually ran.

Rank the no-signal conclusions last (`success` 3, `neutral` 4, `skipped`
5) and pull the order out of its three duplicated copies
(checks-panel-content, PullRequestPage, GitHubItemDialog) into
`src/shared/pr-check-severity-order.ts`. Unknown conclusions now sink to
the bottom instead of silently ranking as `neutral`.

* fix(checks): look up check ranks through a Map, not an object literal

An object-literal rank table resolves `constructor`, `toString`, and
`__proto__` off Object.prototype, so those keys returned a function
instead of falling through to UNKNOWN_CHECK_RANK — the comparator then
subtracted functions, went NaN, and left the list in arbitrary order.
Conclusions come from provider payloads, so keep the lookup on a Map and
cover prototype property names in the test.

* test(checks): cover provider-neutral ordering states

* fix(checks): preserve actionable provider states

* fix(checks): preserve unresolved provider rollups

* fix(checks): keep unknown GitLab rollups neutral

* fix: preserve neutral review check summaries

* fix: complete provider-neutral check ordering remediation

* fix: use provider-neutral mobile review status input

* fix: hydrate GitLab mobile review status

* fix: type mobile GitLab review hydration

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
2026-07-30 13:29:41 -07:00
Brennan Benson 624c8d4120 Make plain-text file:// links clickable in the terminal (#9467)
* Make plain-text file:// links clickable in the terminal

Printed file:// URIs (e.g. a report path echoed by a tool or agent) were
neither http links nor bare filesystem paths, so the terminal's URL and
local-path detectors both skipped them and the link was dead.

Orca already resolves and opens file:// URIs for OSC 8 hyperlinks. Reuse
that exact resolver for plain-text URIs so a printed file:// behaves the
same whether or not the emitter wrapped it in an escape sequence:

- Promote the (dependency-pure) file-url target resolver into src/shared
  so the OSC path and the new plain-text path share one implementation.
- Add a file:// detector that decodes the URI to a filesystem path and
  routes it through the existing file-link pipeline (existence probe +
  openDetectedFilePath), so line/col anchors, %20, Windows drive paths,
  html-in-browser, editor reveal, and SSH/runtime resolution all just work.

Lines without file:// are unchanged: the pass short-circuits to the prior
result, so only file://-bearing lines gain a link.

- Add unit + integration coverage for detection, decoding, and no-double-link.

* Harden plain-text file URI detection

* Split terminal file link detection modules
2026-07-19 15:21:06 -07:00
NeilandOrca f311307560 Add max-lines ratchet CI gate to block new line-limit bypasses (#7608)
oxlint already fails any file over max-lines that is not suppressed, so the
only way to grow past the budget is to add an eslint/oxlint-disable max-lines
comment or a per-file max-lines bump in mobile/.oxlintrc.json. This adds a CI
gate that freezes the current set of suppressions (config/max-lines-baseline.txt,
355 grandfathered entries) and fails the build when a NEW one appears — with a
loud, actionable message pointing at 'split the file'. Existing oversized files
are untouched; the baseline may only shrink (pnpm check:max-lines-ratchet --prune).

Wired into the root lint script and as a dedicated pr.yml step. Unit-tested
(15 cases) and verified against all three failure paths + clean-tree pass.

Co-authored-by: Orca <help@stably.ai>
2026-07-06 17:12:27 -07:00