Commit Graph
74 Commits
Author SHA1 Message Date
Neil 5d14edf4c5 Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	config/max-lines-baseline.txt
2026-08-25 02:25:08 -07:00
Neil 33587fb77d refactor(git): split status.ts into source-control modules (#16393) 2026-08-25 02:24:11 -07:00
Neil 127fa7fae0 refactor(ipc): split repos.ts into focused modules (#16392)
* refactor(ipc): split repos.ts into focused modules

* test: point repo notification mocks at the extracted module

* fix(ipc): repoint the child-process allowlists after the repos split

The type-only `import type { ChildProcess }` moved from repos.ts to
repos/repo-clone-lifecycle.ts, so the import-boundary entry follows it and the
windows-console entry (now stale, and that list only shrinks) is dropped.
Fixture-only; the base file had no runtime child_process use at all.
2026-08-25 02:23:53 -07:00
Neil 8217e6838f refactor runtime contracts and web transports (#16197)
* refactor runtime contracts and transports

* test(web): repoint two-phase timeout seam at the transport that now owns call()
2026-08-25 00:59:29 -07:00
Neil d456549c22 refactor runtime Git bridges (#16189)
* refactor runtime git bridges

* fix(test): avoid computed namespace import access in runtime Git client contract test
2026-08-25 00:54:43 -07:00
Neil f482725355 refactor terminal coordination modules (#16335)
* Refactor terminal coordination modules

* preserve terminal completion and stale-connect guards

* restore pre-spawn E2E barrier and stale-connect check order in ipc-pty-connect

* restore merge-base title-working replay and stamped-tail delete semantics

* fix(terminal): merge the duplicated shortcut-matching import

Two adjacent imports of the same module tripped oxlint's
no-duplicate-imports under --deny-warnings. Import-only; no behavior change.
2026-08-25 00:53:54 -07:00
Neil fc0d947983 refactor(rate-limits): split Codex and Claude fetchers (#16140)
* refactor(rate-limits): split Codex and Claude fetchers

* refactor(rate-limits): restore base error-message defaulting

The split moved the 'Unknown error' fallback from inside String() to the call site, which changed behavior for an Error with an empty .message: base surfaced '', head surfaced 'Unknown error'. Restore the base form.
2026-08-25 00:53:44 -07:00
Neil 5ebc4bb2bc refactor(renderer): split repos store slice (#16191)
* refactor(renderer): split repos store slice

* test(store): align repos/folder-workspace characterization tests with main's owner-scoped delete + host-qualified visit recency

* fix(composer-state): repoint RepoUpdate import to repos/repo-state after split
2026-08-25 00:42:59 -07:00
Neil a782935ab3 refactor(checks): split panel responsibilities (#16186)
* refactor(checks): split panel responsibilities

* fix(checks): consolidate comment audience import

* fix(checks): handle open review state exhaustively

* fix(checks): restore merge-base check presentation values

The split into check-presentation.tsx silently changed icon, opacity, and
PR-state token values. Restore them verbatim.
2026-08-25 00:33:36 -07:00
Neil 83ffc0df24 refactor Electron facilities modules (#16333)
* refactor oversized Electron facilities

* fix interactive process timeout and shortcut repeat guard

* chore(child-process): drop stale cli-installer allowlist entry

cli-installer.ts now routes privileged spawns through runProcess via
cli-privileged-processes.ts, so the shrink-only ratchet flags it as stale.

* refactor(child-process): extract the bounded output sink

runProcess's timeoutMs opt-out (required to preserve the unbounded osascript
admin prompt) pushed run-process.ts past the 300-line cap. Move createOutputSink
to its own module rather than add a max-lines bypass, which AGENTS.md forbids.
Moved verbatim; no behavior change.
2026-08-25 00:31:31 -07:00
Neil 1cf562deea refactor: split source control AI modules (#16179) 2026-08-25 00:31:06 -07:00
Neil e0bc4e1126 refactor(editor): split notebook, editor, and external-watch surfaces (#16143)
* refactor(editor): split editor and watch surfaces

* fix(editor): revert behavior changes smuggled into the surface split

Restore merge-base React keys in IpynbCellOutputs: the content-identity keys
JSON.stringify'd every output value, including raw base64 image payloads, on
every keystroke.

Collapse the duplicated lazy() declarations into editor-lazy-views so each
viewer keeps a single React.lazy identity across the extracted surfaces.
2026-08-25 00:30:47 -07:00
Neil 5c3df831b7 refactor composer and jira slice modules (#16196) 2026-08-24 23:49:08 -07:00
Neil 2935ac3670 refactor(git): split runner.ts into focused command-runner modules 2026-08-24 23:31:33 -07:00
Neil 48e63c015f refactor agent config and auth services (#16195)
* refactor: split agent config and auth services

* chore: repoint wsl and global-fetch guards at split module paths

* fix: restore merge-base Claude CLI error propagation

Drop the secret-redaction rewriting added to Claude CLI error paths in the
refactor: spawn errors again reject with the original Error (preserving
.code/.errno/.syscall/.stack) and command output/auth-status logs are no
longer rewritten.
2026-08-24 23:15:01 -07:00
Neil 32ffdd2b9a refactor(renderer): split composer state (#16182)
* refactor(renderer): split composer state

* fix(renderer): satisfy composer static analysis

* test(renderer): migrate composer boundary contracts

* fix(composer): restore project group reset effect

Revert read-side mask back to the merge-base state clear so a momentarily
unavailable host permanently drops the folder group instead of silently
retargeting Create when the host reappears.
2026-08-24 23:14:42 -07:00
Neil c4988fa81c refactor workspace lifecycle modules (#16334)
* refactor(workspaces): split lifecycle modules

* preserve workspace cleanup consent contract

* restore workspace delete shortcut hint in context menu view

* restore host-qualified visit recency and viewed-candidate predicate

* test(cleanup): pin the viewed-mark upgrade path and host-qualified visit reads

Two invariants a refactor broke in this PR, both silent:

- viewed marks are persisted, so gating `shouldPreserveCleanupInspection` on any
  newer field voids the grace period for every entry written by an older build
- visits are stamped under `${hostId}|${worktreeId}` whenever the host is known
  (the normal case, including 'local'), so a bare map[worktreeId] read misses
  every modern entry and yields 0, disabling the recent-visible-context blocker

Verified discriminating: reintroducing each bug fails exactly its own test.
2026-08-24 22:44:08 -07:00
Neil 8043b5f705 Refactor worktrees IPC into cohesive modules (#16190)
* refactor worktrees IPC into cohesive modules

* test update PTY waiver invariant paths

* revert unrelated oxfmt reflow from merge commit
2026-08-24 21:19:11 -07:00
Neil d8807801ff Refactor GitHub Project surfaces and mutations (#16166)
* refactor github project modules

* restore dropped issue #4756 guard comments
2026-08-24 21:18:55 -07:00
Neil 087b895524 refactor(daemon): split oversized PTY services (#16160)
* refactor(daemon): split oversized PTY services

* revert(daemon): restore merge-base session listing and canceled-spawn behavior

Two behavior changes rode along with the file-splitting refactor:

- listLiveTerminalHostSessions dropped sessions with isTerminating, not just
  dead ones, hiding sessions the merge base still advertised.
- spawnAndPublishSession called session.beginTermination() before publishing a
  canceled spawn into the host map.

Both hunks are reverted to the merge base; the refactor is untouched.
2026-08-24 21:18:11 -07:00
Neil 5869e6d39e refactor Linear workspace surfaces (#16332)
* refactor Linear workspace surfaces

* refactor(linear): restore merge-base behavior in split modules

The Linear surface split smuggled in three behavior changes; revert them
so the refactor is a pure move.

- detail-state: drop 'project' from EDITED_LINEAR_ISSUE_FIELDS. List
  issues never carry `project` (only getIssue maps it), so preserving it
  across hydration permanently blanked the hydrated project whenever an
  edit landed while linearGetIssue was in flight.
- detail-state: handleProjectChanged no longer sets hasEditedRef.
- project-selector: remove the mountedRef/requestId guards around the
  global patchLinearIssue write and the success/error toasts.
- sub-issues: remove the added isComposing guard on the title Enter key.

The detail-state test asserted the smuggled project-preservation; updated
to assert hydration owns `project`.
2026-08-24 20:57:53 -07:00
Neil ec12a0e442 refactor settings maintenance modules (#16169)
* refactor settings maintenance modules

* revert behavior changes smuggled into settings split

- hoist isAdvancedOpen state back into RepositoryHooksSection so it survives
  SearchableSetting unmount during settings search
- drop isComposing guards absent from the merge-base AgentsPane handlers
- restore merge-base JSX for the 'when one exists.' fragment (no separator)
2026-08-24 20:57:37 -07:00
Neil d74f469379 refactor(feature-wall): split animated browser and terminal stories (#16142)
* refactor feature wall animated visuals

* fix(feature-wall): restore merge-base render behavior in split visuals

- Hoist workbench reduced-motion state to module constants so cursorTarget
  identity is stable and the cursor layout effect stops re-firing per render.
- Render one frame component and branch on the state source so toggling
  reducedMotion re-renders the storyboard instead of remounting its DOM.
2026-08-24 20:57:21 -07:00
Neil b6a24ecac1 refactor: split GitHub backend modules (#16194) 2026-08-24 20:46:07 -07:00
Neil 9bcaf09869 refactor loading store into cohesive domains (#16192) 2026-08-24 20:45:53 -07:00
Neil b516300b8c refactor agent hook listener modules (#16187) 2026-08-24 20:45:38 -07:00
Neil 2c77f71c4f refactor(renderer): split IPC event bridges (#16185)
* refactor(renderer): split IPC event bridges

* test(renderer): follow extracted IPC shortcut bridge
2026-08-24 20:45:23 -07:00
Neil f57114a106 refactor(renderer): split terminal store slice (#16184) 2026-08-24 20:45:08 -07:00
Neil d3c37b8e40 refactor(renderer): split web preload API (#16181) 2026-08-24 20:44:53 -07:00
Neil 07b13c8468 refactor git repository host boundaries (#16177) 2026-08-24 20:44:34 -07:00
Neil 2960fe9193 Split relay entrypoints into focused modules (#16151) 2026-08-24 19:55:57 -07:00
Neil 1c436a8084 refactor(rpc): split terminal methods into cohesive modules (#16188) 2026-08-24 19:52:12 -07:00
Neil 8776b6e49a refactor(renderer): split GitHub store slice (#16183) 2026-08-24 19:52:04 -07:00
Neil 4371aaf722 refactor provider clients into domain modules (#16168) 2026-08-24 19:51:57 -07:00
Neil 0b66daffcc refactor(cli): split orchestration handlers (#16139) 2026-08-24 19:51:40 -07:00
Jinjing 3bc13f7b8c Split monolithic PTY IPC module into organized submodules (#15172)
* rm unused files

* remove unused files

* Refactor PTY IPC and add host environment paths

- Split PTY handlers out of inline baseline checks
- Rename local PTY shell provider for clarity
- Pass userDataPath and resourcesPath to host environment

* Establish PTY daemon identity before first await in spawn flow

Move identity setup, session ID minting, and hidden delivery state to
the beginning of preflight, ensuring these complete synchronously
before any awaited operations. Defer async operations like folder
workspace validation; add liveness tracking for SSH provider failures.
Refactor pane spawn reservation to prevent concurrent spawns from
creating duplicate providers.

* Add incarnationId tracking throughout PTY exit lifecycle

Track PTY incarnation IDs in exit messages sent to renderer, and add cause tracking for exit events. This enables proper lifecycle state management when PTYs can be respawned or have multiple concurrent instances. Also adds deadline support to process listing operations and stop-request tracking for better shutdown observability.

* Use fake timers in SFTP namespace tests for deterministic abort handling

Tests now use `vi.useFakeTimers()` to control time during abort scenarios,
advancing timers explicitly instead of waiting on real async delays. Ensures
more reliable test execution without flakiness from timing-dependent behavior.

* Fix PTY spawn lifecycle: handle concurrent races and cleanup abandoned a

Properly release Agent Teams leader handles when spawns are abandoned or fail,
restore provisional PTY sizes on reattachment, and settle concurrent spawn races
for the same pane. Add validation guards for destroyed renderers and improve
handler re-registration to reset delivery state before bridging a new window.

* Move PTY cleanup to localized error boundaries

Restore provisional PTY size when build-options fails and guard pre-allocated handle registration. This ensures cleanup happens at the point of error, not deferred to the general catch block.

* Replace Promise.resolve() with vi.waitFor in PTY claim test

Wait explicitly for the providerSpawn call to be made using vi.waitFor()
instead of relying on event-loop yielding. This makes the test more
deterministic and reduces flakiness from timing assumptions.

* Redact PTY IDs in pending data drop diagnostics

Prevent workspace paths embedded in session IDs from leaking through
diagnostic logs by using redactPtyIdForDiagnostics.

* Mark PTY exit events as observed by provider

Exit handlers now receive `providerExitObserved: true` to
distinguish definitive provider-witnessed exits from inferred
state changes. Preserves optional exit cause when present.

* Add defensive input validation to PTY IPC handlers

Validate that IPC arguments are present and the correct type before
passing them to handler logic. Uses optional chaining and type checks
to safely handle malformed requests from the renderer process.

* Replace direct Electron imports with PTY host bindings

Abstract app, ipcMain, and powerMonitor access through getter functions
to support multiple host environments and improve testability.

* Defend against transient PTY setup failures with state cleanup

Host-env setup failures now trigger cleanup of runtime-allocated PTY state. Cached PTY geometry is preserved after transient reattach failures but cleared when the provider reports the PTY exited before the spawn reply—preventing stale geometry from corrupting future operations. Error handling now distinguishes expired SSH sessions and early-exit conditions to preserve geometry appropriately.
2026-08-24 15:30:16 -07:00
Jinjing 7a72f341f7 Split pty-connection.ts into focused modules under 400 lines (#15166)
* refactor: split pty-connection.ts under 400 lines

* rm design doc

* refactor(pty-connection): extract reattach payload handlers as factories

- Replace bindApplyReattachPayload with createReattachPayloadHandlers factory that returns handlers instead of mutating session directly, enabling better composability and testing
- Extract waitForUserInitiatedSshConnect as standalone function for reuse across deferred session attach flows
- Create ReattachPayloadSession type to document and isolate required session capabilities
- Add test coverage for overlapping reattach payload attempts
- Clean up comments to remove redundant prefixes (session.pane → pane, session.transport → transport)

* fix(pty-connection): correct sequencing and state bugs in spawn and reat

- Fix terminal tail slice to take prefix instead of suffix, preserving escape
  sequence markers needed by next scan
- Clear pending pane serializer when direct SSH retry PTY is unclaimed
- Initialize interrupt status baseline to undefined so first input advances
  sequence counter
- Bump reattach generation only after confirming current attempt owns the stream,
  preventing superseded results from canceling in-flight prepaint

* fix(pty-connection): correct sequencing and state bugs in spawn and reat

- Fix terminal tail slice to take prefix instead of suffix, preserving escape
  sequence markers needed by next scan
- Clear pending pane serializer when direct SSH retry PTY is unclaimed
- Initialize interrupt status baseline to undefined so first input advances
  sequence counter
- Bump reattach generation only after confirming current attempt owns the stream,
  preventing superseded results from canceling in-flight prepaint

* fix(test): increase poll iterations to prevent Node 26 test leakage

Increase event loop turns from 40 to 200 in the timer settlement loop.
Node 26's libuv poll phase can briefly starve when concurrent workers
transform tests, causing cleanup to leak into the next test. The higher
iteration count ensures async operations complete before returning.

* fix(foreground-output-budgets): use >= for budget window boundary check

At the exact window boundary, the budget should roll over. Change the
comparison from > to >= so the window resets when now equals
windowStart + FOREGROUND_BUDGET_WINDOW_MS, not just after. Add tests
to verify budget rejection and rollover behavior.

* refactor(pty-connection): add status observations and routing improvemen

- Track agent status observations with origin and transition metadata
- Separate interactive redraw input timing from general terminal input
- Restore pane authority on bind and reattach
- Refine routing trust and confirmation state handling
- Invoke queued startup callbacks when PTY is bound
- Resolve Windows shell overrides with user settings

* refactor: extract resolveLaunchAgentCandidate helper

Consolidate duplicated launch-agent resolution logic into a shared helper to prevent future divergence between paneExpectsLaunchAgent and resolveExpectedLaunchTuiAgent.

* refactor(pty-connection): use model snapshot for direct SSH reconnects

Direct SSH reconnects now restore from the full SSH model snapshot (complete scrollback) when dimensions are compatible, instead of the bounded relay tail. Falls back gracefully when incompatible or alternate-screen was exited.

* refactor(pty): retry unverifiable SSH reattaches via preserved bindings

Preserve deferred SSH session IDs longer when they serve as the only retry binding,
allowing the system to attempt recovery through direct SSH retries or PTY remounts
when reattach fails in an unverifiable way. Simplify reconnect model restoration
by removing the conditional model snapshot probe and using relay replay directly.

* test: poll terminal readiness in expectSingleOwningPty

Retry the terminal list assertion with polling to account for timing
delays in PTY state reporting from the runtime.
2026-08-23 13:55:20 -07:00
Jinjing 63dbf12d14 Split github client (#15214)
* refactor(github-client): reorganize client into lifecycle folders

* refactor(github-client): extract PR refresh data and outcome assembly

Separate the derived data calculation and outcome assembly logic from
branch-lookup-resolution into dedicated modules for better separation of
concerns. Modernize type import syntax and format exports consistently.

* refactor(github-client): improve error handling and resilience

Defensive GraphQL parsing prevents partial responses from breaking REST fallbacks.
Cache failures now use shorter TTLs for faster recovery. PR operations have
dedicated error classification. GraphQL mutations track rate limit usage to prevent
quota exhaustion. Data validation improved to reject spurious values.

* Extract check rerun error classification with operation context

Create classifyRerunChecksError() to provide operation-specific error
messages when check reruns fail. This replaces generic GitHub error
copy with context appropriate to what the user attempted (rerun
checks). Follows the pattern of classifyListPrsError and improves
error handling by delegating extraction to extractExecError.

* Make check-rerun not-found error message resource-neutral

Error handling for failed check reruns now covers both workflow-run
reruns and standalone check-run rerequests. Tests verify the neutral
message works for both scenarios.
2026-08-17 21:18:44 -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