Skip to content

feat(web): add Run-in-Worktree toggle to mobile AutoRun launch - #946

Merged
pedramamini merged 6 commits into
RunMaestro:rcfrom
chr1syy:feat/web-autorun-worktree
May 10, 2026
Merged

feat(web): add Run-in-Worktree toggle to mobile AutoRun launch#946
pedramamini merged 6 commits into
RunMaestro:rcfrom
chr1syy:feat/web-autorun-worktree

Conversation

@chr1syy

@chr1syy chr1syy commented May 3, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds the Dispatch to a separate worktree toggle (with base-branch picker, sanitized new-branch input, and optional "create PR on completion") to the mobile Auto Run launch sheet — closing the AR-PARITY-02 gap with desktop's WorktreeRunSection.
  • Two new SSH-aware WebSocket messages, get_git_branches and list_worktrees, proxy git directly from the main process using each session's cwd + sessionSshRemoteConfig.
  • The mobile launch path threads the composed worktree payload through configure_auto_run, where the desktop's existing remote-handler already creates the worktree, checks out the branch, and (optionally) opens a PR.

What changed

Backend (src/main/web-server/)

  • handlers/messageHandlers.ts — new get_git_branches and list_worktrees handlers with sessionId validation, "not configured" fallback, and requestId echo.
  • types.tsSessionData / SessionBroadcastData gain isGitRepo + worktreeBasePath; new GitBranchesResult, WorktreeEntry, ListWorktreesResult, and the corresponding callback type aliases.
  • managers/CallbackRegistry.ts, WebServer.ts — registry slots and setter exposure for the new callbacks.
  • web-server-factory.ts — emits isGitRepo + worktreeBasePath on the sessions list and wires the two new callbacks via execGit (SSH-aware) on the session's cwd.

Frontend (src/web/)

  • hooks/useAutoRun.ts — new LaunchWorktreeConfig, WorktreeSummary, loadGitBranches, listWorktrees; launchAutoRun spreads worktree only when enabled === true.
  • hooks/useWebSocket.tsSessionData gains isGitRepo + worktreeBasePath.
  • mobile/AutoRunWorktreeSection.tsx (new) — toggle, base-branch picker, sanitized branch-name input with path preview, PR checkbox; gated on isGitRepo + worktreeBasePath.
  • mobile/AutoRunSetupSheet.tsx, mobile/App.tsx — section rendered above the Prompt, loaders bound to the active session.

Tests

  • __tests__/main/web-server/handlers/messageHandlers.test.ts — six new cases (validation, payload shape, callback-not-configured) for the two WS handlers.
  • __tests__/web/hooks/useAutoRun.test.ts (new) — launchAutoRun worktree forwarding/strip + branch/worktree request shape and error fallbacks.
  • __tests__/web/mobile/AutoRunWorktreeSection.test.tsx (new) — render gating, enable→config emission, PR toggle, branch-cleared→null.
  • Updated __tests__/web/mobile/App.test.tsx mock to expose the new hook methods.

Test plan

  • Mobile (web): open Auto Run setup on a git-repo session with worktree base path configured; toggle "Dispatch to a separate worktree", confirm branches load and the path preview matches <basePath>/<branchName>.
  • Launch with the toggle on → desktop creates the worktree, checks out the new branch, runs the Auto Run inside it.
  • Toggle "Automatically create PR when complete" on → PR is opened against the chosen base branch on completion.
  • Section is hidden for non-git sessions and disabled (with helper text) when no worktreeBasePath is configured.
  • SSH remote session: branches load via the session's SSH remote (sessionSshRemoteConfig).
  • npx vitest run on the scoped suite (158 pass / 0 fail).
  • Lint, Prettier, and TypeScript all clean on touched files.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Mobile Auto Run: dispatch runs into isolated Git worktrees with base-path, branch selector, path preview, and optional auto-PR creation; launches respect worktree enablement and validation.
    • Sessions & lazy loaders: session metadata now surfaced for Git/worktree status and on-demand branch/worktree loading to improve setup responsiveness.
  • Tests

    • Added tests covering Web→Desktop message paths, branch/worktree loaders, and mobile AutoRun UI flows, validations, race conditions, and error handling.

Adds parity with desktop's WorktreeRunSection so mobile users can dispatch
an Auto Run to a separate git worktree, optionally creating a PR on
completion.

- New WS handlers `get_git_branches` and `list_worktrees` (SSH-aware via
  the session's `sessionSshRemoteConfig`).
- Surfaces `isGitRepo` + `worktreeBasePath` on `SessionData` so mobile
  can gate the section and compute the worktree path preview.
- Extends `useAutoRun.launchAutoRun` to forward the optional `worktree`
  payload through `configure_auto_run`, and adds `loadGitBranches` /
  `listWorktrees` helpers.
- New `AutoRunWorktreeSection` mobile component (toggle, base-branch
  picker, sanitized branch input + path preview, "create PR on
  completion" checkbox), wired into `AutoRunSetupSheet` above the
  Prompt section.
- Tests: handler validation/payload shape, hook payload threading, and
  section rendering / config emission.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Run-in-Worktree support end-to-end: new types, WebSocket message handlers and callback wiring, main-process git executors, frontend hook methods to load branches/worktrees and include worktree in Auto Run launches, a mobile UI for worktree configuration, a shared worktree spawn utility, and tests/mocks.

Changes

Run-in-Worktree Feature

Layer / File(s) Summary
Data Shape & Types
src/main/web-server/types.ts, src/web/hooks/useAutoRun.ts, src/web/hooks/useWebSocket.ts
Adds GitBranchesResult, ListWorktreesResult, WorktreeEntry, LaunchWorktreeConfig, WorktreeSummary; extends SessionData/SessionBroadcastData with parentSessionId, worktreeBranch, isGitRepo, worktreeBasePath.
Callback Registry & Types
src/main/web-server/managers/CallbackRegistry.ts
Adds nullable callbacks getGitBranchesForSession / listWorktreesForSession, initializes them to null, adds getters that return empty defaults when unset, and adds setters to register callbacks.
WebServer API Wiring
src/main/web-server/WebServer.ts
Exports setGetGitBranchesForSessionCallback and setListWorktreesForSessionCallback and wires message-handler registration to the registry.
WebSocket Message Handling
src/main/web-server/handlers/messageHandlers.ts
Adds get_git_branches and list_worktrees message routes; implements handleGetGitBranches and handleListWorktrees—validate sessionId, require configured callbacks, invoke callbacks, and reply with git_branches / worktrees_list (echoing requestId) or send an error.
Main-process Git Implementations
src/main/web-server/web-server-factory.ts
Registers callbacks that run git branch -a/git rev-parse and git worktree list --porcelain via execGit, handle SSH context, parse outputs into structured results, and return empty fallbacks on non-zero exit codes.
Frontend Hook Behavior
src/web/hooks/useAutoRun.ts
Adds worktree?: LaunchWorktreeConfig to LaunchConfig; launchAutoRun includes worktree only when enabled; exposes loadGitBranches(sessionId) and listWorktrees(sessionId) that dispatch get_git_branches / list_worktrees and unwrap responses.
Mobile Integration & Sheet
src/web/mobile/App.tsx, src/web/mobile/AutoRunSetupSheet.tsx
Wires loadGitBranches/listWorktrees for active session; AutoRunSetupSheet accepts isGitRepo, worktreeBasePath, loadGitBranches, loadWorktrees, tracks worktreeState, blocks launch on enabled-invalid, and includes worktree in launch config only when enabled-valid.
Mobile Worktree UI
src/web/mobile/AutoRunWorktreeSection.tsx
New component: hidden for non-git sessions, requires worktreeBasePath to enable, loads branches/worktrees, generates sanitized default branch name, computes path preview, validates inputs, and emits discriminated AutoRunWorktreeState (disabled / enabled-invalid / enabled-valid) with LaunchWorktreeConfig.
Shared Spawn Utility
src/renderer/utils/worktreeSpawn.ts
Adds spawnWorktreeAgentAndDispatch to sanitize/validate branch, compute path, call git.worktreeSetup, detect/reuse existing sessions, create new worktree sessions, optionally populate PR config, and return dispatch session id or null on handled errors.
Renderer Batch/Remote Wiring
src/renderer/hooks/batch/useAutoRunHandlers.ts, src/renderer/hooks/remote/useAppRemoteEventListeners.ts
Moves spawn logic to shared utility; when worktree enabled, remote listener spawns worktree child session (resolving parent via parentSessionId when applicable), updates batchConfig with spawn-time mutations, uses spawn session id as dispatch target, and returns immediate IPC success/failure accordingly.
Tests / Mocks
src/__tests__/main/web-server/handlers/messageHandlers.test.ts, src/__tests__/web/hooks/useAutoRun.test.ts, src/__tests__/web/mobile/*, src/__tests__/main/web-server/web-server-factory.test.ts
Adds mocks for new callbacks and WebServer setters; tests for message handlers (validation, forwarding, responses, error), hook behavior (payload shaping, loaders, error propagation), and mobile UI behavior (toggle, defaults, validation, PR option).

Sequence Diagram

sequenceDiagram
    actor User
    participant Mobile as Mobile App
    participant Sheet as AutoRunSetupSheet
    participant Section as AutoRunWorktreeSection
    participant Hook as useAutoRun Hook
    participant WS as WebSocket Client
    participant Server as WebServer
    participant Git as Git Executor

    User->>Section: Enable worktree dispatch
    Section->>Hook: loadGitBranches(sessionId)
    Hook->>WS: sendRequest get_git_branches
    WS->>Server: route to messageHandlers
    Server->>Git: exec "git branch -a" / "git rev-parse"
    Git->>Server: branches + currentBranch
    Server->>WS: git_branches response
    WS->>Hook: deliver response
    Hook->>Section: return {branches,currentBranch}

    Section->>Hook: listWorktrees(sessionId)
    Hook->>WS: sendRequest list_worktrees
    WS->>Server: route to messageHandlers
    Server->>Git: exec "git worktree list --porcelain"
    Git->>Server: worktree entries
    Server->>WS: worktrees_list response
    WS->>Hook: deliver response
    Hook->>Section: return worktrees

    User->>Section: Configure branch, toggle PR
    Section->>Sheet: onChange(LaunchWorktreeConfig)
    User->>Sheet: Tap Launch
    Sheet->>Hook: launchAutoRun(config with worktree)
    Hook->>WS: send configure_auto_run (includes worktree)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

ready to merge

Poem

🐰 I sniff the branches, hop with glee,
New worktrees spring for Auto Run to be.
I tidy names and preview paths so neat,
Push a button—jobs sprout fast on tiny feet.
Hop on, dear devs, and watch your runs take flight!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: adding a Run-in-Worktree toggle to mobile AutoRun launch. It is concise, specific, and clearly summarizes the primary feature addition without unnecessary details.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps

greptile-apps Bot commented May 3, 2026

Copy link
Copy Markdown

Greptile Summary

Adds the "Dispatch to a separate worktree" toggle to the mobile Auto Run launch sheet, closing the AR-PARITY-02 feature gap with the desktop. Two new SSH-aware WebSocket messages (get_git_branches, list_worktrees) proxy git from the main process, and the spawnWorktreeAgentAndDispatch helper is extracted into a shared module so both the desktop handler and the new remote event listener can spawn a correctly-parented child session before delegating to startBatchRun.

The previously flagged P1 (empty prTargetBranch when "Create PR" is enabled before branches finish loading) is now addressed with an explicit guard in AutoRunWorktreeSection's propagation effect and covered by the new slow-loadBranches test case.

Confidence Score: 5/5

Safe to merge — no functional defects found in the new worktree dispatch path or the WS handlers.

The change is well-scoped and follows established patterns. The previously flagged guard against empty prTargetBranch on early PR-toggle is now present and tested. The spawnWorktreeAgentAndDispatch extraction is a straight lift; startBatchRun already has the worktreeTarget-present guard that skips duplicate setupWorktree, confirmed at line 630 of useBatchProcessor.ts. SSH context resolution is consolidated into a single helper rather than copy-pasted. Only minor style observations remain.

No files require special attention.

Important Files Changed

Filename Overview
src/web/mobile/AutoRunWorktreeSection.tsx New component: toggle, base-branch picker, branch-name input, path preview, PR checkbox; correctly guards against empty baseBranch + createPR, and uses cancellation tokens for async effects.
src/main/web-server/web-server-factory.ts Adds isGitRepo/worktreeBasePath to session broadcast and registers both new git callbacks; SSH resolution extracted into resolveSessionGitContext helper, eliminating the previously-noted duplication.
src/renderer/hooks/remote/useAppRemoteEventListeners.ts New worktree dispatch block before startBatchRun: spawns child session via spawnWorktreeAgentAndDispatch, forwards worktreeTarget to suppress duplicate setupWorktree in useBatchProcessor; verified correct against useBatchProcessor's worktreeTarget guard at line 630.
src/renderer/utils/worktreeSpawn.ts Logic extracted verbatim from useAutoRunHandlers; getSshRemoteId now typed Session (non-null) which is safer than the old Session
src/main/web-server/handlers/messageHandlers.ts Adds handleGetGitBranches and handleListWorktrees with sessionId validation and requestId echo; the callback-presence guard is unreachable at runtime (WebServer always supplies a non-null wrapper), but harmless.
src/main/web-server/managers/CallbackRegistry.ts Adds getGitBranchesForSession and listWorktreesForSession registry methods; silently returns empty results when callbacks aren't set.
src/web/mobile/AutoRunSetupSheet.tsx Integrates AutoRunWorktreeSection above the prompt input; blocks launch when worktreeState is enabled-invalid; resets worktreeState on session change/close.
src/web/hooks/useAutoRun.ts Adds LaunchWorktreeConfig, WorktreeSummary, loadGitBranches, listWorktrees; worktree config only forwarded when enabled===true; transport errors propagate correctly.
src/tests/web/mobile/AutoRunWorktreeSection.test.tsx Comprehensive tests covering toggle gating, branch loading, PR checkbox, branch-cleared to invalid, and the baseBranch-empty+createPR race (slow-loadBranches case).

Sequence Diagram

sequenceDiagram
    participant MobileUI as Mobile UI (AutoRunWorktreeSection)
    participant WS as WebSocket (useAutoRun)
    participant WSHandler as WS Handler (messageHandlers.ts)
    participant Factory as web-server-factory.ts
    participant RemoteListener as useAppRemoteEventListeners
    participant Spawn as worktreeSpawn.ts
    participant BatchProc as useBatchProcessor

    MobileUI->>WS: get_git_branches / list_worktrees (sessionId)
    WS->>WSHandler: handleGetGitBranches / handleListWorktrees
    WSHandler->>Factory: getGitBranchesForSession(sessionId)
    Factory->>Factory: resolveSessionGitContext (SSH-aware)
    Factory->>Factory: execGit branch -a cwd sshRemote
    Factory-->>WSHandler: GitBranchesResult
    WSHandler-->>MobileUI: git_branches response

    MobileUI->>WS: configure_auto_run (worktree payload)
    WS->>RemoteListener: configure_auto_run event
    RemoteListener->>RemoteListener: resolve parentSession if worktree child
    RemoteListener->>Spawn: spawnWorktreeAgentAndDispatch(parent, spawnConfig)
    Spawn->>Spawn: git worktreeSetup create-new mode
    Spawn->>Spawn: buildWorktreeSession add to sessionStore
    Spawn-->>RemoteListener: newSessionId
    RemoteListener->>RemoteListener: set batchConfig.worktreeTarget skip flag
    RemoteListener->>BatchProc: startBatchRun(newSessionId, batchConfig)
    BatchProc->>BatchProc: worktreeTarget present skip setupWorktree
    BatchProc->>BatchProc: run Auto Run on worktree session
Loading

Reviews (2): Last reviewed commit: "fix(web): spawn worktree child against l..." | Re-trigger Greptile

Comment thread src/main/web-server/handlers/messageHandlers.ts Outdated
Comment thread src/main/web-server/handlers/messageHandlers.ts Outdated
Comment thread src/main/web-server/web-server-factory.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/web-server/web-server-factory.ts`:
- Around line 1433-1453: The catch block in getGitBranchesForSession (the try
that awaits execGit for ['branch', '-a'...] and ['rev-parse'...]) currently
swallows unexpected failures and returns { branches: [] }; instead, change the
behavior so that genuine execution or SSH errors are not collapsed into an
empty-branches success result: rethrow the caught error (or return an explicit
error-shaped result the client can detect) so callers can distinguish infra/SSH
failures from a real empty repo; similarly update the handling around non-zero
execGit exits in the same function (and the analogous worktree function that
uses execGit/parseGitBranches/parseGitWorktrees) to only map successful
exitCode===0 to parsed results and to propagate other failures upstream (e.g.,
via throw or an error result object) rather than returning { branches: [] } / {
worktrees: [] }.

In `@src/web/hooks/useAutoRun.ts`:
- Around line 163-193: The current implementations of loadGitBranches and
listWorktrees swallow all errors from sendRequest and return fake empty results;
instead let transport/backend failures bubble up so callers can show an error
state. Remove or change the empty-catching behavior in loadGitBranches and
listWorktrees (the try/catch blocks around sendRequest) so that unexpected
errors are rethrown (or rethrown with added context referencing the operation,
e.g., "get_git_branches" / "list_worktrees") rather than returning `{ branches:
[] }` or `[]`; keep the existing return shapes on success and ensure sendRequest
errors propagate to callers (or wrap and throw a new Error) so Sentry/upper
layers can observe failures.

In `@src/web/mobile/AutoRunSetupSheet.tsx`:
- Line 60: AutoRunSetupSheet treats a null worktreeConfig as "omit worktree"
which lets an invalid/cleared AutoRunWorktreeSection silently fall back to
launching against the main checkout; change the worktree state to an explicit
discriminated union (e.g. { status: "disabled" } | { status: "enabled", config:
LaunchWorktreeConfig } | { status: "invalid", errors?: string[] }) instead of
nullable, update AutoRunWorktreeSection's onChange to emit those three cases
(disabled, enabled-with-config, enabled-but-invalid), and update the launch
payload construction in AutoRunSetupSheet (the code paths around the current
worktreeConfig handling) to block launch when status === "invalid" and only omit
the worktree payload for status === "disabled" while using the config when
status === "enabled".

In `@src/web/mobile/AutoRunWorktreeSection.tsx`:
- Around line 88-93: The catch blocks in loadBranches and loadWorktrees silently
swallow errors; update both .catch handlers to forward the caught exception to
Sentry by importing and calling captureException from src/utils/sentry.ts (e.g.,
catch((err) => { captureException(err, { context: { function: 'loadBranches' }
}); ... }) for loadBranches and similarly for loadWorktrees), while preserving
existing UI state changes like setBranchLoadError(true) and setBranches([]).
- Around line 288-293: The select currently shows "Loading..." whenever
branches.length === 0, which also occurs after a failed fetch because branches
is reset to [], so change the condition to reflect actual loading vs error
states: render the "Loading..." option only when branchLoading is true (or when
!branchLoadError && branches.length === 0), and render an error placeholder
option (e.g., "Failed to load branches") when branchLoadError is true; update
the JSX around the branches.map block (referencing branches, branchLoading,
branchLoadError in AutoRunWorktreeSection) so the select distinguishes
in-progress vs failed fetches.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 582d86ae-e66d-4d4a-8d50-cefbeb74a659

📥 Commits

Reviewing files that changed from the base of the PR and between 1338860 and fc44d64.

📒 Files selected for processing (14)
  • src/__tests__/main/web-server/handlers/messageHandlers.test.ts
  • src/__tests__/web/hooks/useAutoRun.test.ts
  • src/__tests__/web/mobile/App.test.tsx
  • src/__tests__/web/mobile/AutoRunWorktreeSection.test.tsx
  • src/main/web-server/WebServer.ts
  • src/main/web-server/handlers/messageHandlers.ts
  • src/main/web-server/managers/CallbackRegistry.ts
  • src/main/web-server/types.ts
  • src/main/web-server/web-server-factory.ts
  • src/web/hooks/useAutoRun.ts
  • src/web/hooks/useWebSocket.ts
  • src/web/mobile/App.tsx
  • src/web/mobile/AutoRunSetupSheet.tsx
  • src/web/mobile/AutoRunWorktreeSection.tsx

Comment thread src/main/web-server/web-server-factory.ts Outdated
Comment thread src/web/hooks/useAutoRun.ts
Comment thread src/web/mobile/AutoRunSetupSheet.tsx Outdated
Comment thread src/web/mobile/AutoRunWorktreeSection.tsx Outdated
Comment thread src/web/mobile/AutoRunWorktreeSection.tsx Outdated
CI failure fix:
- Add `setGetGitBranchesForSessionCallback` / `setListWorktreesForSessionCallback`
  to the WebServer mock in `web-server-factory.test.ts` so the factory's
  callback registration doesn't blow up the suite.

Review feedback (greptile + coderabbit):
- `messageHandlers.ts`: defensively coerce non-Error rejections in the two new
  handlers (`error instanceof Error ? error.message : String(error)`).
- `web-server-factory.ts`: extract `resolveSessionGitContext()` helper to
  deduplicate SSH resolution and drop the no-op `|| undefined`. Stop
  swallowing exec/SSH failures into empty-result success — let them
  propagate so Sentry sees real infra/SSH regressions; only `exitCode !== 0`
  (legitimately "not a git repo") still maps to empty results.
- `useAutoRun.ts`: `loadGitBranches` / `listWorktrees` no longer swallow
  transport errors; let rejections propagate so the section can render an
  actual error state.
- `AutoRunWorktreeSection.tsx`: log caught exceptions via `webLogger.error`,
  replace `branchLoadError` boolean with a `branchLoadStatus`
  ('idle' | 'loading' | 'error') tri-state so the picker shows
  "Failed to load" instead of a stuck "Loading…" after a fetch failure.
- `AutoRunWorktreeSection.tsx` + `AutoRunSetupSheet.tsx`: `onChange` now
  emits a discriminated `AutoRunWorktreeState` union
  ('disabled' | 'enabled-valid' | 'enabled-invalid'). The sheet blocks
  launch and shows an inline warning when status is 'enabled-invalid' —
  prevents an enabled-but-invalid worktree config from silently falling
  through to a normal Auto Run on the main checkout.

Tests:
- Updated `AutoRunWorktreeSection.test.tsx` to assert the new state union
  and added a "Failed to load" placeholder regression test.
- Updated `useAutoRun.test.ts` to assert the new error-propagation contract
  for `loadGitBranches` / `listWorktrees`.

Scoped vitest: 196 pass / 0 fail. Lint, prettier, type-check clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/__tests__/main/web-server/web-server-factory.test.ts (1)

348-410: ⚡ Quick win

Add registration assertions for the new git/worktree callbacks.

The mock includes the two new setters, but callback-registration tests don’t currently assert they were wired during factory setup.

Suggested test addition
 it('should register file and auto-run callbacks', () => {
 	expect(server.setOpenFileTabCallback).toHaveBeenCalled();
 	expect(server.setRefreshFileTreeCallback).toHaveBeenCalled();
 	expect(server.setRefreshAutoRunDocsCallback).toHaveBeenCalled();
 	expect(server.setConfigureAutoRunCallback).toHaveBeenCalled();
+	expect(server.setGetGitBranchesForSessionCallback).toHaveBeenCalled();
+	expect(server.setListWorktreesForSessionCallback).toHaveBeenCalled();
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/main/web-server/web-server-factory.test.ts` around lines 348 -
410, Add assertions that the two new git/worktree callback setters were called
during factory setup: add
expect(server.setGetGitStatusCallback).toHaveBeenCalled(); and
expect(server.setSwitchWorktreeCallback).toHaveBeenCalled(); (or use the exact
mock setter names if they differ) into the callback registrations tests—either
in the existing "file and auto-run callbacks" test or a new small it block so
the factory wiring for setGetGitStatusCallback and setSwitchWorktreeCallback is
asserted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/web-server/web-server-factory.ts`:
- Around line 1418-1427: resolveSessionGitContext currently falls back to local
git when session.sessionSshRemoteConfig.enabled is true but
getSshRemoteById(remoteId) returns undefined; change it to fail fast instead:
inside resolveSessionGitContext (and referencing StoredSession,
session.sessionSshRemoteConfig.remoteId and getSshRemoteById), detect the case
where enabled is true but sshRemoteId is missing or getSshRemoteById returns
undefined and throw a descriptive error (or return an explicit failure) rather
than returning a local context; ensure callers handle the thrown error/explicit
failure so remote-backed sessions don't silently use local git data.

---

Nitpick comments:
In `@src/__tests__/main/web-server/web-server-factory.test.ts`:
- Around line 348-410: Add assertions that the two new git/worktree callback
setters were called during factory setup: add
expect(server.setGetGitStatusCallback).toHaveBeenCalled(); and
expect(server.setSwitchWorktreeCallback).toHaveBeenCalled(); (or use the exact
mock setter names if they differ) into the callback registrations tests—either
in the existing "file and auto-run callbacks" test or a new small it block so
the factory wiring for setGetGitStatusCallback and setSwitchWorktreeCallback is
asserted.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b8897577-67ec-47f1-a0f0-8a47fa346d63

📥 Commits

Reviewing files that changed from the base of the PR and between fc44d64 and 9ce0e33.

📒 Files selected for processing (8)
  • src/__tests__/main/web-server/web-server-factory.test.ts
  • src/__tests__/web/hooks/useAutoRun.test.ts
  • src/__tests__/web/mobile/AutoRunWorktreeSection.test.tsx
  • src/main/web-server/handlers/messageHandlers.ts
  • src/main/web-server/web-server-factory.ts
  • src/web/hooks/useAutoRun.ts
  • src/web/mobile/AutoRunSetupSheet.tsx
  • src/web/mobile/AutoRunWorktreeSection.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/web/hooks/useAutoRun.ts

Comment on lines +1418 to +1427
const resolveSessionGitContext = (
session: StoredSession
): { sshRemote: ReturnType<typeof getSshRemoteById>; remoteCwd: string | undefined } => {
const sshRemoteId = session.sessionSshRemoteConfig?.enabled
? session.sessionSshRemoteConfig.remoteId
: undefined;
const sshRemote = sshRemoteId ? getSshRemoteById(sshRemoteId) : undefined;
const remoteCwd = sshRemote ? session.cwd : undefined;
return { sshRemote, remoteCwd };
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast when SSH is enabled but the remote cannot be resolved.

If session.sessionSshRemoteConfig.enabled is true but remoteId is missing/stale, this currently falls back to local git execution. That can return incorrect branch/worktree data for remote-backed sessions.

Suggested fix
 const resolveSessionGitContext = (
 	session: StoredSession
 ): { sshRemote: ReturnType<typeof getSshRemoteById>; remoteCwd: string | undefined } => {
-	const sshRemoteId = session.sessionSshRemoteConfig?.enabled
-		? session.sessionSshRemoteConfig.remoteId
-		: undefined;
-	const sshRemote = sshRemoteId ? getSshRemoteById(sshRemoteId) : undefined;
-	const remoteCwd = sshRemote ? session.cwd : undefined;
-	return { sshRemote, remoteCwd };
+	if (!session.sessionSshRemoteConfig?.enabled) {
+		return { sshRemote: undefined, remoteCwd: undefined };
+	}
+	const sshRemoteId = session.sessionSshRemoteConfig.remoteId;
+	if (!sshRemoteId) {
+		throw new Error(`SSH remote is enabled but remoteId is missing for session ${session.id}`);
+	}
+	const sshRemote = getSshRemoteById(sshRemoteId);
+	if (!sshRemote) {
+		throw new Error(`SSH remote not found: ${sshRemoteId}`);
+	}
+	return { sshRemote, remoteCwd: session.cwd };
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/web-server/web-server-factory.ts` around lines 1418 - 1427,
resolveSessionGitContext currently falls back to local git when
session.sessionSshRemoteConfig.enabled is true but getSshRemoteById(remoteId)
returns undefined; change it to fail fast instead: inside
resolveSessionGitContext (and referencing StoredSession,
session.sessionSshRemoteConfig.remoteId and getSshRemoteById), detect the case
where enabled is true but sshRemoteId is missing or getSshRemoteById returns
undefined and throw a descriptive error (or return an explicit failure) rather
than returning a local context; ensure callers handle the thrown error/explicit
failure so remote-backed sessions don't silently use local git data.

Resolves conflicts where Run-in-Worktree extensions and the upstream
Auto Run parity work both touched the same surfaces:

- src/main/web-server/types.ts (SessionData / SessionBroadcastData)
- src/main/web-server/web-server-factory.ts (sessions emit + new git callbacks)
- src/web/hooks/useAutoRun.ts (LaunchConfig, exports, state shape)
- src/web/hooks/useWebSocket.ts (SessionData)
- src/web/mobile/App.tsx (useAutoRun destructuring + sheet props wiring)
- src/web/mobile/AutoRunSetupSheet.tsx (imports, props, reset effect, layout)

All conflicts resolved by keeping both sides' fields/handlers — Run-in-
Worktree props live alongside upstream's playbook/error-recovery wiring.

Also addresses one new review comment on commit 9ce0e33:
- web-server-factory.ts:resolveSessionGitContext now fails loudly when
  SSH is enabled but the configured remote can't be resolved instead
  of silently falling back to local git (would have leaked the run to
  the wrong machine for SSH-backed sessions).

Updated the App.test.tsx useAutoRun mock to expose the playbook /
error-recovery / worktree methods that App.tsx now destructures.

Scoped vitest: 188 pass / 0 fail. Lint, prettier, type-check clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/web-server/handlers/messageHandlers.ts`:
- Around line 3101-3104: The catch blocks in messageHandlers.ts that currently
convert backend failures into websocket-only errors use this.sendError(...) and
should instead report the error to Sentry via reportHandlerError(...) so
failures are captured; modify the two catch handlers (the one calling
this.sendError(client, `Failed to get git branches: ${msg}`) and the similar
block at the other location) to call reportHandlerError(error, context) (or
reportHandlerError(new Error(msg), { handler: 'git/worktree', client }))
before/alongside sending a websocket error, using the existing
reportHandlerError helper and preserving the original client/sendError behavior
if needed so diagnostics are recorded.

In `@src/main/web-server/web-server-factory.ts`:
- Around line 1622-1626: The current logic treats execGit failures (where
execGit returns exitCode as a string like "ENOENT"/"EPERM") as harmless empty
results by mapping branchesResult/currentBranchResult to []/undefined; instead
detect when exitCode is a non-numeric string and surface the error (throw or
return the original error) so it does not get downgraded to an empty repo state.
Update the code paths that use execGit (references: execGit, branchesResult,
currentBranchResult, parseGitBranches and the analogous worktreesResult
handling) to check typeof exitCode === "string" and rethrow a descriptive Error
(or propagate the original error object) before falling back to parsing stdout
for numeric exitCode === 0 cases. Ensure identical handling is applied to the
worktrees block mentioned (lines 1649-1653).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 814f95e9-2e04-4d71-a7b0-f962f63cfb44

📥 Commits

Reviewing files that changed from the base of the PR and between 9ce0e33 and f304407.

📒 Files selected for processing (12)
  • src/__tests__/main/web-server/handlers/messageHandlers.test.ts
  • src/__tests__/main/web-server/web-server-factory.test.ts
  • src/__tests__/web/mobile/App.test.tsx
  • src/main/web-server/WebServer.ts
  • src/main/web-server/handlers/messageHandlers.ts
  • src/main/web-server/managers/CallbackRegistry.ts
  • src/main/web-server/types.ts
  • src/main/web-server/web-server-factory.ts
  • src/web/hooks/useAutoRun.ts
  • src/web/hooks/useWebSocket.ts
  • src/web/mobile/App.tsx
  • src/web/mobile/AutoRunSetupSheet.tsx
✅ Files skipped from review due to trivial changes (5)
  • src/tests/main/web-server/web-server-factory.test.ts
  • src/main/web-server/types.ts
  • src/tests/main/web-server/handlers/messageHandlers.test.ts
  • src/tests/web/mobile/App.test.tsx
  • src/web/hooks/useWebSocket.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/web/mobile/AutoRunSetupSheet.tsx
  • src/web/hooks/useAutoRun.ts
  • src/main/web-server/WebServer.ts
  • src/web/mobile/App.tsx

Comment thread src/main/web-server/handlers/messageHandlers.ts
Comment thread src/main/web-server/web-server-factory.ts
…t spawn failures

Addresses two CodeRabbit review comments on f304407:

- messageHandlers.ts (handleGetGitBranches / handleListWorktrees): the
  new `.catch` blocks were calling `this.sendError` directly, which
  surfaces a websocket error to the client but never reaches Sentry.
  Routed both through `reportHandlerError(...)` so production
  diagnostics see SSH/git regressions; the user-facing message and
  shape are unchanged.

- web-server-factory.ts (getGitBranchesForSession /
  listWorktreesForSession): `execGit` returns `exitCode: number |
  string`, where a string code (ENOENT, EPERM, …) means git never
  ran. The previous fallback collapsed those failures into
  `{ branches: [] }` / `{ worktrees: [] }`, making broken local/SSH
  execution look like a healthy empty repo. Now we explicitly throw
  on a non-numeric exitCode so the real cause propagates and only
  numeric non-zero codes (legitimate "not a git repo") still map to
  empty results.

Scoped vitest: 188 pass / 0 fail. Lint, prettier, type-check clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chr1syy

chr1syy commented May 4, 2026

Copy link
Copy Markdown
Contributor Author

Blocker: AR-PARITY-02 gap not actually closed — mobile/web launch skips child-session creation that desktop does

Found while soaking #946 into 0.16.0-RC-build for a 0.16.17-RC test build.

The PR description says this closes the AR-PARITY-02 gap with desktop's WorktreeRunSection. The on-disk worktree creation, branch checkout, and "agent runs in the worktree" parts work correctly. But the UI affordance that makes the desktop flow actually usable — a new child agent session in the Left Bar that the user can switch to — is missing on the mobile/web path. From a mobile user's perspective, they tap "Dispatch to a separate worktree", AutoRun runs, and nothing visible happens in their Left Bar. There's no agent for them to navigate to or continue interacting with after the AutoRun.

Evidence

Desktop pathsrc/renderer/hooks/batch/useAutoRunHandlers.ts:427-457:

} else if (
    config.worktreeTarget?.mode === 'create-new' ||
    config.worktreeTarget?.mode === 'existing-closed'
) {
    let parentForSpawn = activeSession;
    if (activeSession.parentSessionId) {
        const parent = selectSessionById(activeSession.parentSessionId)(useSessionStore.getState());
        if (parent) parentForSpawn = parent;
    }
    const newSessionId = await spawnWorktreeAgentAndDispatch(parentForSpawn, config);
    if (!newSessionId) return;
    targetSessionId = newSessionId;
}
// ...
startBatchRun(targetSessionId, config, activeSession.autoRunFolderPath);

spawnWorktreeAgentAndDispatch creates a new child session with parentSessionId linking it to the original. That child shows up in the Left Bar; startBatchRun is dispatched to its ID.

Mobile/web pathsrc/renderer/hooks/remote/useAppRemoteEventListeners.ts:306-341:

const worktree: BatchRunConfig['worktree'] | undefined =
    config.worktree && config.worktree.enabled
        ? { enabled: true, path: ..., branchName: ..., createPROnCompletion: ..., prTargetBranch: ... }
        : undefined;

const batchConfig: BatchRunConfig = {
    documents,
    prompt: config.prompt || DEFAULT_BATCH_PROMPT,
    loopEnabled: config.loopEnabled || false,
    maxLoops: config.maxLoops,
    ...(worktree ? { worktree } : {}),
};
// ...
startBatchRun(sessionId, batchConfig, folderPath).catch(...)

startBatchRun is called with the original sessionId. No spawnWorktreeAgentAndDispatch. The worktree is created on disk and the spawned agent's cwd is set to it (which is why my probe playbook reported cwd: /home/chris/code/worktrees/main-test, is worktree: yes, git dirgit common dir), but no child agent appears in the UI.

Repro

  1. Mobile/web AutoRun setup on a git session with worktreeBasePath configured.
  2. Toggle "Dispatch to a separate worktree" on, pick a base branch, type a fresh branch name, leave PR-on-completion off.
  3. Pick the diagnostic playbook (one task that prints pwd / git rev-parse --git-dir / git rev-parse --git-common-dir).
  4. Launch.

Observed:

  • Worktree directory exists on disk after launch ✅
  • Agent runs inside it (cwd matches <basePath>/<branch>, is worktree: yes) ✅
  • Left Bar still shows only the original agent. No child session, no new entry, no toast. ❌
  • The AutoRun completes; user has no UI surface to continue interacting with the agent that ran in the worktree.

Suggested fix

Mirror desktop's path in the remote handler: before calling startBatchRun, when config.worktree?.enabled === true, spawn a child session via spawnWorktreeAgentAndDispatch (or its session-creating equivalent) and use the returned newSessionId as the dispatch target. Propagate that ID back to the mobile client in the configure_auto_run_result so the mobile client can navigate to the new session.

The minimum diff is roughly:

// In useAppRemoteEventListeners.ts, before startBatchRun:
let dispatchSessionId = sessionId;
if (worktree?.enabled) {
    const newSessionId = await spawnWorktreeAgentAndDispatch(parentForSpawn, batchConfig);
    if (!newSessionId) {
        sendRemoteConfigureAutoRunResponse(responseChannel, { success: false, error: 'Worktree spawn failed' });
        return;
    }
    dispatchSessionId = newSessionId;
}
sendRemoteConfigureAutoRunResponse(responseChannel, { success: true, sessionId: dispatchSessionId });
startBatchRun(dispatchSessionId, batchConfig, folderPath).catch(...);

…plus the corresponding mobile-client-side handling of the returned sessionId so the user can switch to the new agent. Tests in __tests__/renderer/hooks/batch/useAutoRunHandlers.worktree.test.ts already cover the desktop path; mirror equivalents for the remote handler would be useful.

Other findings from the same soak (non-blocking, FYI)

  • ✅ Section gating: hidden for non-git sessions; disabled (with helper) when no worktreeBasePath.
  • ✅ Branch list loads from the session's git over get_git_branches; SSH path not yet exercised here.
  • ✅ Path preview sanitization: feat: foo bar!foo-bar! (colon + feat: prefix dropped, space→hyphen).
  • ⚠️ Sanitization preserves ! — git allows it, but it's a shell-special character (history expansion) that may surprise users when they cd to the worktree path interactively. Strictness call.

@chr1syy chr1syy added the blocking Can't move forward due to some hurdle. label May 4, 2026
@chr1syy

chr1syy commented May 4, 2026

Copy link
Copy Markdown
Contributor Author

Afternote — child session IS being spawned, but attached to the wrong parent

Correction to my prior comment: a child session does get added to the Left Bar after a mobile/web worktree launch — it just attaches to the agent immediately above the launching agent in the Left Bar, not to the launching agent itself. I missed this on the first read because the new entry blended in among my many existing worktree sessions; chr1syy spotted it on a closer look at the Left Bar after the run.

That makes this two distinct issues, not one:

  1. Wrong-parent attachment. The newly created child session links to a sibling/predecessor agent rather than the agent that actually launched the AutoRun. Likely a parent-resolution bug — possibly an off-by-one in whatever logic walks/derives the parent on the remote launch path, or a stale activeSession snapshot at the moment the spawn fires. Suspect path: the parentForSpawn resolution that prefers activeSession.parentSessionId may be reading from the wrong session object on the remote path.

  2. The earlier diagnosis still partially stands. I had pattern-matched the symptom "no UI affordance" to "no spawn at all" based on the snippet in useAppRemoteEventListeners.ts:306-341, which doesn't call spawnWorktreeAgentAndDispatch. There must be another path inside startBatchRun (or downstream of it) that does spawn for the remote launch — that's what's producing the wrong-parent child. The earlier fix-suggestion ("call spawnWorktreeAgentAndDispatch before startBatchRun") was therefore incomplete; the right fix is probably to align the remote launch path's spawn call to use the same parentForSpawn resolution as useAutoRunHandlers.ts:427-457, threading the launching session through correctly.

Repro for the wrong-parent attachment

  1. Have multiple agents in the Left Bar.
  2. Launch a worktree-mode AutoRun via mobile/web from a specific agent (let's call it agent X).
  3. After launch, observe the Left Bar: the new child session attaches to agent X-1 (or a sibling) instead of agent X.

Keeping the blocking label — both issues land on the same launch path and should be fixed together.

… AutoRun

Mobile/web AutoRun's "Dispatch to a separate worktree" was relying on the
chokidar watcher in useWorktreeHandlers to discover the new on-disk
worktree and attach it to a parent. The watcher's find-first heuristic
attached the child to whichever sibling agent's worktreeConfig.basePath
matched first, producing the wrong-parent attachment reported in PR RunMaestro#946.

Extract spawnWorktreeAgentAndDispatch into a shared util and call it
from the remote configureAutoRun handler, mirroring desktop's
useAutoRunHandlers parent resolution. The remote path now spawns the
child session explicitly with the correct parentSessionId before
startBatchRun runs, and forwards worktreeTarget so startBatchRun skips
its redundant setupWorktree.

Also closes Greptile P1: AutoRunWorktreeSection now emits enabled-invalid
when createPROnCompletion is true but baseBranch is still empty (race
window between loadBranches resolution and the user toggling PR
creation).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/renderer/hooks/remote/useAppRemoteEventListeners.ts (1)

423-446: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

batchConfig.worktree.path may not match the spawned worktree when createPROnCompletion is false.

spawnWorktreeAgentAndDispatch only writes back into config.worktree when target.createPROnCompletion is true (see worktreeSpawn.ts lines 175–184). When the mobile launch sets worktree.enabled with createPROnCompletion=false, batchConfig.worktree.path/branchName retain the values the CLI/web client sent — which may differ from the path/branch the helper actually resolved (e.g. the user typed Cue Dashboard and the helper sanitized it to cue-dashboard, or the branch already existed at a different worktree path returned via result.existingPath).

Because dispatch happens against targetSessionId (whose cwd is correct), the run itself is fine, but downstream consumers reading batchConfig.worktree.path/branchName will see stale values. Two safe options:

  1. Always update batchConfig.worktree from the helper's resolved values (mirror what worktreeTarget would tell us), or
  2. Drop batchConfig.worktree entirely on the createPROnCompletion=false path and rely on worktreeTarget + the spawned session's cwd.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/hooks/remote/useAppRemoteEventListeners.ts` around lines 423 -
446, batchConfig.worktree can contain stale path/branch values because
spawnWorktreeAgentAndDispatch (see worktreeSpawn.ts) may canonicalize or return
an existingPath/branchName but we never copy those back into batchConfig; update
useAppRemoteEventListeners.ts so that after calling
spawnWorktreeAgentAndDispatch (or wherever you receive the spawn result) you
overwrite batchConfig.worktree.path and batchConfig.worktree.branchName with the
resolved values from the spawn result (e.g., result.existingPath /
result.branchName) whenever a worktree was created/resolved (mirror what
worktreeTarget would expose); alternatively, if you choose option 2, remove
batchConfig.worktree when worktree.createPROnCompletion is false so downstream
consumers rely on worktreeTarget/targetSessionId cwd instead—implement one of
these two fixes in the code path that calls spawnWorktreeAgentAndDispatch.
♻️ Duplicate comments (1)
src/web/mobile/AutoRunWorktreeSection.tsx (1)

101-109: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

captureException still absent from both catch blocks — coding guideline violation.

webLogger.error was added (improvement over the previous commit), but captureException from src/utils/sentry.ts is still missing in both handlers. Per the guideline, recoverable network errors should still be tracked via Sentry with context.

🛡️ Proposed fix — add captureException in both catch blocks
+import { captureException } from '../../utils/sentry';

 // branch-load catch
     .catch((err: unknown) => {
         if (cancelled) return;
         webLogger.error(
             `Failed to load branches: ${err instanceof Error ? err.message : String(err)}`,
             'AutoRunWorktreeSection'
         );
+        captureException(err instanceof Error ? err : new Error(String(err)), {
+            tags: { context: 'AutoRunWorktreeSection.loadBranches' },
+        });
         setBranches([]);
         setBranchLoadStatus('error');
     });

 // worktree-load catch
     .catch((err: unknown) => {
         if (cancelled) return;
         webLogger.error(
             `Failed to list worktrees: ${err instanceof Error ? err.message : String(err)}`,
             'AutoRunWorktreeSection'
         );
+        captureException(err instanceof Error ? err : new Error(String(err)), {
+            tags: { context: 'AutoRunWorktreeSection.loadWorktrees' },
+        });
         setExistingWorktrees([]);
     });

As per coding guidelines: "Use Sentry utilities (captureException, captureMessage) from src/utils/sentry.ts for explicit error reporting with context."

Also applies to: 130-137

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/web/mobile/AutoRunWorktreeSection.tsx` around lines 101 - 109, The catch
blocks inside AutoRunWorktreeSection currently log errors with webLogger.error
but omit Sentry reporting; import captureException from src/utils/sentry.ts and
call captureException(err, { component: 'AutoRunWorktreeSection', action: 'load
branches' }) in the catch that wraps the branches loading (the handler that
currently calls setBranches([]) and setBranchLoadStatus('error')), and likewise
add captureException(err, { component: 'AutoRunWorktreeSection', action: 'load
worktrees' }) in the other catch (the handler around lines 130-137) so
recoverable network errors are reported with contextual metadata while retaining
the existing webLogger.error and state updates.
🧹 Nitpick comments (3)
src/renderer/utils/worktreeSpawn.ts (3)

132-135: 💤 Low value

Dead fallback: branchName is always set before this point.

In the create-new branch we early-return when sanitizeGitBranchName produces an empty string (lines 56–64), and in existing-closed we set branchName = worktreePath.split('/').pop() || 'worktree' (line 111) which is always truthy. So if (!branchName && gitBranches && ...) can never fire. Either drop the block or move it before the existing-closed fallback so the git-detected branch is preferred over the path basename.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/utils/worktreeSpawn.ts` around lines 132 - 135, The fallback
that sets branchName from gitBranches is dead because branchName is always set
earlier; move the block that picks gitBranches[0] (the if (!branchName &&
gitBranches && gitBranches.length > 0) {...}) to execute before the
existing-closed fallback that sets branchName = worktreePath.split('/').pop() ||
'worktree' (or simply remove the block if you prefer path basename to win). In
practice, relocate the git-detected branch selection so it runs after
sanitizeGitBranchName and before the worktreePath basename assignment
(references: branchName, gitBranches, sanitizeGitBranchName, worktreePath) so
the git-detected branch is preferred.

65-68: 💤 Low value

Path derivation breaks on Windows local sessions.

parentSession.cwd.replace(/\/[^/]+$/, '') + '/worktrees' and worktreePath = basePath + '/' + branchName assume POSIX separators. On a Windows local session where cwd is C:\Users\me\repo, the regex won't match (no forward slashes), basePath becomes C:\Users\me\repo/worktrees, and the resulting path mixes separators. Same concern at line 111 with worktreePath.split('/').pop().

This isn't a regression (the logic was extracted from the pre-existing handler with the same shape), so flagging it as a follow-up rather than a blocker. SSH sessions and macOS/Linux are unaffected.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/utils/worktreeSpawn.ts` around lines 65 - 68, The path logic
assumes POSIX separators and breaks on Windows; update the worktree path
derivation in worktreeSpawn.ts to use Node's path utilities: import path from
'path' and compute basePath with either
path.join(parentSession.worktreeConfig.basePath, ...) when
worktreeConfig.basePath exists or path.join(path.dirname(parentSession.cwd),
'worktrees') when falling back, then set worktreePath = path.join(basePath,
branchName); likewise replace worktreePath.split('/').pop() with
path.basename(worktreePath) so separator handling is correct on Windows and
POSIX environments.

125-130: ⚡ Quick win

Remove duplicate Sentry capture from gitService.getBranches call.

gitService.getBranches is implemented via createIpcMethod with defaultValue: [] and without rethrow: true. This configuration causes the IPC layer to swallow failures internally and report them to Sentry (via captureException in ipcWrapper.ts:108). The try/catch block at lines 125–130 is dead code—the call never throws, and any exception would already be captured by the IPC wrapper.

Remove the try/catch and simplify:

-	// Step 3: Fetch git info for the worktree
-	let gitBranches: string[] | undefined;
-	try {
-		gitBranches = await gitService.getBranches(worktreePath, sshRemoteId);
-	} catch (err) {
-		// Non-fatal — git info is nice-to-have
-		captureException(err, { extra: { worktreePath, sshRemoteId } });
-	}
+	// Step 3: Fetch git info for the worktree (errors reported via IPC layer).
+	const gitBranches = await gitService.getBranches(worktreePath, sshRemoteId);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/utils/worktreeSpawn.ts` around lines 125 - 130, Remove the dead
try/catch around gitService.getBranches in worktreeSpawn.ts: since
createIpcMethod for getBranches uses defaultValue: [] and doesn't rethrow, the
IPC wrapper already captures errors (ipcWrapper.ts captureException at the IPC
layer), so delete the try/catch and just assign gitBranches = await
gitService.getBranches(worktreePath, sshRemoteId); leaving the defaultValue
behavior intact and avoiding a duplicate Sentry capture.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/__tests__/web/mobile/AutoRunWorktreeSection.test.tsx`:
- Around line 235-236: The test is resolving the pending promise by calling
resolveBranches!(...) outside React's act(), which triggers state setters
(setBranches, setBaseBranch, setNewBranchName, setBranchLoadStatus) on
AutoRunWorktreeSection and can produce act() warnings; fix it by wrapping the
resolveBranches!({ branches: ['main'], currentBranch: 'main' }) call inside
act(() => { ... }) (or await act(async () => { ... }) if needed) so the
component state updates occur within React's act boundary.

In `@src/renderer/hooks/remote/useAppRemoteEventListeners.ts`:
- Around line 482-488: spawnWorktreeAgentAndDispatch currently returns null on
failure which causes sendRemoteConfigureAutoRunResponse (via
window.maestro.process.sendRemoteConfigureAutoRunResponse) to always send the
generic 'Failed to spawn worktree agent'; change spawnWorktreeAgentAndDispatch
to return a tagged result (e.g. { sessionId: string } | { error: string | Error
} or set a small ref with lastError) and then update this handler to inspect
that result and forward the specific error string (or result.error) in the
sendRemoteConfigureAutoRunResponse payload instead of the hardcoded message so
the mobile client receives the real failure reason.
- Around line 480-520: The success response currently omits the spawned agent
session ID so the mobile client cannot navigate; add an optional sessionId?:
string to the IPC response type in the renderer type declaration and preload API
(update the response type in global.d.ts and the process.ts signature), then
modify the renderer call to
window.maestro.process.sendRemoteConfigureAutoRunResponse to include sessionId:
targetSessionId when targetSessionId !== original sessionId (or when
targetSessionId is set), and update the web-server/message handler that emits
configure_auto_run_result to propagate that sessionId through the
configure_auto_run_result payload so callers receive the new session ID.

In `@src/web/mobile/AutoRunWorktreeSection.tsx`:
- Line 225: branchNameMissing is currently set to enabled &&
!newBranchName.trim() which triggers a "Branch name is required" warning while
branches are still loading; update the validation in AutoRunWorktreeSection to
also check branchLoadStatus (use the existing branchLoadStatus variable) and
only mark branchNameMissing true when branchLoadStatus indicates loading is
finished (e.g., branchLoadStatus === 'loaded' or !== 'loading') so the warning
does not flash before a default branch name is seeded.

---

Outside diff comments:
In `@src/renderer/hooks/remote/useAppRemoteEventListeners.ts`:
- Around line 423-446: batchConfig.worktree can contain stale path/branch values
because spawnWorktreeAgentAndDispatch (see worktreeSpawn.ts) may canonicalize or
return an existingPath/branchName but we never copy those back into batchConfig;
update useAppRemoteEventListeners.ts so that after calling
spawnWorktreeAgentAndDispatch (or wherever you receive the spawn result) you
overwrite batchConfig.worktree.path and batchConfig.worktree.branchName with the
resolved values from the spawn result (e.g., result.existingPath /
result.branchName) whenever a worktree was created/resolved (mirror what
worktreeTarget would expose); alternatively, if you choose option 2, remove
batchConfig.worktree when worktree.createPROnCompletion is false so downstream
consumers rely on worktreeTarget/targetSessionId cwd instead—implement one of
these two fixes in the code path that calls spawnWorktreeAgentAndDispatch.

---

Duplicate comments:
In `@src/web/mobile/AutoRunWorktreeSection.tsx`:
- Around line 101-109: The catch blocks inside AutoRunWorktreeSection currently
log errors with webLogger.error but omit Sentry reporting; import
captureException from src/utils/sentry.ts and call captureException(err, {
component: 'AutoRunWorktreeSection', action: 'load branches' }) in the catch
that wraps the branches loading (the handler that currently calls
setBranches([]) and setBranchLoadStatus('error')), and likewise add
captureException(err, { component: 'AutoRunWorktreeSection', action: 'load
worktrees' }) in the other catch (the handler around lines 130-137) so
recoverable network errors are reported with contextual metadata while retaining
the existing webLogger.error and state updates.

---

Nitpick comments:
In `@src/renderer/utils/worktreeSpawn.ts`:
- Around line 132-135: The fallback that sets branchName from gitBranches is
dead because branchName is always set earlier; move the block that picks
gitBranches[0] (the if (!branchName && gitBranches && gitBranches.length > 0)
{...}) to execute before the existing-closed fallback that sets branchName =
worktreePath.split('/').pop() || 'worktree' (or simply remove the block if you
prefer path basename to win). In practice, relocate the git-detected branch
selection so it runs after sanitizeGitBranchName and before the worktreePath
basename assignment (references: branchName, gitBranches, sanitizeGitBranchName,
worktreePath) so the git-detected branch is preferred.
- Around line 65-68: The path logic assumes POSIX separators and breaks on
Windows; update the worktree path derivation in worktreeSpawn.ts to use Node's
path utilities: import path from 'path' and compute basePath with either
path.join(parentSession.worktreeConfig.basePath, ...) when
worktreeConfig.basePath exists or path.join(path.dirname(parentSession.cwd),
'worktrees') when falling back, then set worktreePath = path.join(basePath,
branchName); likewise replace worktreePath.split('/').pop() with
path.basename(worktreePath) so separator handling is correct on Windows and
POSIX environments.
- Around line 125-130: Remove the dead try/catch around gitService.getBranches
in worktreeSpawn.ts: since createIpcMethod for getBranches uses defaultValue: []
and doesn't rethrow, the IPC wrapper already captures errors (ipcWrapper.ts
captureException at the IPC layer), so delete the try/catch and just assign
gitBranches = await gitService.getBranches(worktreePath, sshRemoteId); leaving
the defaultValue behavior intact and avoiding a duplicate Sentry capture.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 437ffd73-02c5-41e7-80dd-971368538751

📥 Commits

Reviewing files that changed from the base of the PR and between 8930644 and 9ac39dd.

📒 Files selected for processing (5)
  • src/__tests__/web/mobile/AutoRunWorktreeSection.test.tsx
  • src/renderer/hooks/batch/useAutoRunHandlers.ts
  • src/renderer/hooks/remote/useAppRemoteEventListeners.ts
  • src/renderer/utils/worktreeSpawn.ts
  • src/web/mobile/AutoRunWorktreeSection.tsx

Comment thread src/__tests__/web/mobile/AutoRunWorktreeSection.test.tsx Outdated
Comment on lines +480 to 520
try {
const newSessionId = await spawnWorktreeAgentAndDispatch(parentForSpawn, spawnConfig);
if (!newSessionId) {
window.maestro.process.sendRemoteConfigureAutoRunResponse(responseChannel, {
success: false,
error: 'Failed to spawn worktree agent',
});
return;
}
targetSessionId = newSessionId;
// spawnWorktreeAgentAndDispatch may rewrite spawnConfig.worktree
// (e.g. when the on-disk worktree already existed at a different
// path or createPROnCompletion was requested). Reflect that on
// the batchConfig forwarded to startBatchRun.
if (spawnConfig.worktree) {
batchConfig.worktree = spawnConfig.worktree;
}
// Setting worktreeTarget tells startBatchRun to skip its own
// setupWorktree call — the spawn helper already created the
// directory and built the session.
batchConfig.worktreeTarget = spawnConfig.worktreeTarget;
} catch (err) {
logger.error('[Remote] Failed to spawn worktree agent:', undefined, err);
notifyToast({
type: 'error',
title: 'Worktree Error',
message: err instanceof Error ? err.message : String(err),
});
window.maestro.process.sendRemoteConfigureAutoRunResponse(responseChannel, {
success: false,
error: err instanceof Error ? err.message : String(err),
});
return;
}
}

// Send success response immediately - startBatchRun is long-running
// and would exceed the IPC/CLI timeout if awaited.
window.maestro.process.sendRemoteConfigureAutoRunResponse(responseChannel, {
success: true,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the configure-auto-run response type and check the WS handler/mobile consumer.
rg -nP -C3 'sendRemoteConfigureAutoRunResponse' --type=ts
rg -nP -C3 'configure_auto_run_result|ConfigureAutoRunResponse|RemoteConfigureAutoRunResponse' --type=ts

Repository: RunMaestro/Maestro

Length of output: 24517


Add sessionId to the success response so the mobile client can navigate to spawned agents.

After a worktree spawn, the mobile caller needs the new session ID to display and navigate to the spawned child session. Today the success path sends { success: true } (line 518–520) with no ID, leaving the mobile UI unable to switch to the agent running the batch.

The response type currently lacks this field. Update the type definition in src/renderer/global.d.ts (line 378) and src/main/preload/process.ts (line 671–672) to include sessionId?: string, then plumb it through:

  • Renderer: pass targetSessionId when it differs from the original sessionId (line 518–520)
  • Web-server handler: propagate the new ID in configure_auto_run_result (messageHandlers.ts, line 1587–1590)

This is a blocking acceptance criterion per the PR objectives.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/hooks/remote/useAppRemoteEventListeners.ts` around lines 480 -
520, The success response currently omits the spawned agent session ID so the
mobile client cannot navigate; add an optional sessionId?: string to the IPC
response type in the renderer type declaration and preload API (update the
response type in global.d.ts and the process.ts signature), then modify the
renderer call to window.maestro.process.sendRemoteConfigureAutoRunResponse to
include sessionId: targetSessionId when targetSessionId !== original sessionId
(or when targetSessionId is set), and update the web-server/message handler that
emits configure_auto_run_result to propagate that sessionId through the
configure_auto_run_result payload so callers receive the new session ID.

Comment on lines +482 to +488
if (!newSessionId) {
window.maestro.process.sendRemoteConfigureAutoRunResponse(responseChannel, {
success: false,
error: 'Failed to spawn worktree agent',
});
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Generic 'Failed to spawn worktree agent' swallows the helper's specific reason.

When spawnWorktreeAgentAndDispatch returns null it has already shown a desktop toast with the actual cause (invalid branch name, busy target agent, worktreeSetup failure with result.error, etc.), but the mobile client only sees the generic string here. Consider having the helper return a tagged result (or read the most recent error from a small ref) so the WS response can propagate the real reason — otherwise the mobile UX is "Failed to spawn worktree agent" with no actionable detail.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/renderer/hooks/remote/useAppRemoteEventListeners.ts` around lines 482 -
488, spawnWorktreeAgentAndDispatch currently returns null on failure which
causes sendRemoteConfigureAutoRunResponse (via
window.maestro.process.sendRemoteConfigureAutoRunResponse) to always send the
generic 'Failed to spawn worktree agent'; change spawnWorktreeAgentAndDispatch
to return a tagged result (e.g. { sessionId: string } | { error: string | Error
} or set a small ref with lastError) and then update this handler to inspect
that result and forward the specific error string (or result.error) in the
sendRemoteConfigureAutoRunResponse payload instead of the hardcoded message so
the mobile client receives the real failure reason.

Comment thread src/web/mobile/AutoRunWorktreeSection.tsx Outdated
@chr1syy

chr1syy commented May 5, 2026

Copy link
Copy Markdown
Contributor Author

@greptileai re-review please

- useAppRemoteEventListeners: stop pre-populating batchConfig.worktree
  with the raw mobile payload. The user-typed branch name and computed
  path can drift from the values spawnWorktreeAgentAndDispatch resolves
  (sanitized branch, or an existingPath returned by `git worktree add`).
  Mirror the desktop launch shape: leave batchConfig.worktree undefined
  unless the spawn helper writes it back (createPROnCompletion=true),
  rely on worktreeTarget + the spawned session's cwd otherwise.

- worktreeSpawn: drop the dead `!branchName && gitBranches[0]` fallback
  (branchName is always set before that point) and remove the duplicate
  Sentry capture from the gitService.getBranches call — the IPC wrapper
  already reports failures via captureException. Keep a bare try/catch
  to preserve the non-fatal contract.

- AutoRunWorktreeSection: gate the "Branch name is required" warning on
  branchLoadStatus !== 'loading' so it doesn't flash before the seeded
  default arrives.

- AutoRunWorktreeSection.test: wrap the trailing `resolveBranches!`
  resolution in act() so its component state updates are inside React's
  act boundary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@chr1syy

chr1syy commented May 7, 2026

Copy link
Copy Markdown
Contributor Author

Wrong-parent fix verified — unblocking

Re-tested 2026-05-07 against the 0.16.0-RC-build soak build (PR #946 with 9ac39ddd + c1d06a3e).

What's fixed

  • Child session is now correctly parented to the launching agent. The wrong-parent attachment regression from yesterday's report is gone. Verified by launching WORKTREE-TEST-01.md against a specific agent X (git repo + worktreeBasePath configured) — diagnostic playbook reported cwd: ~/code/worktrees/rc-test, is worktree: yes, branch: rc-test, and git worktree list includes the new entry.
  • ✅ Worktree creation on disk, branch checkout, agent's cwd inside the worktree — all intact.

Removing the blocking label.

Non-blocking follow-ups (mobile/web parity gaps)

Two UI affordance gaps surfaced during the same soak — these don't block this PR but should be tracked as follow-up work:

  1. Launching agent's state indicator doesn't change after launch — stays green (idle) instead of flipping to busy/connecting. Mobile users get no in-UI confirmation the launch took.
  2. Spawned child session not visible in mobile's Left Bar like on desktop — desktop renders the parent/child relationship, mobile doesn't.

Filed as a gist with repro and suggested fix directions: https://gist.github.com/chr1syy/67630166ff5217a97d7b1fad91201f3b

Suggested fix directions in the gist: Center Flash + jump-session click action for the launch ack (Gap 1), and mirroring desktop's parentSessionId rendering on the mobile/web session list (Gap 2). The Center Flash + clickAction pattern is already established in the codebase, so Gap 1 is a small lift.

Other items still untested in this pass (deferred from yesterday's plan, not part of this PR's blocker history):

  • PR-on-completion toggle → real PR opens against the chosen base branch.
  • Branch-cleared edge: clearing the input → expect worktree: null payload, no worktree.
  • SSH-remote session: branches load via SSH, worktree creation on the remote host.

These can be exercised in a follow-up pass once the gist's items are addressed.

@chr1syy chr1syy added ready to merge This PR is ready to merge and removed blocking Can't move forward due to some hurdle. labels May 7, 2026
chr1syy added a commit to chr1syy/Maestro that referenced this pull request May 9, 2026
…h indicator

- useAutoRun.ts: extend the configure_auto_run timeout to 60s when
  worktree dispatch is enabled (greptile P1). Worktree launches block
  on `git worktree add` plus an upstream getBranches round-trip on the
  server, which routinely exceeds the default 10s and surfaces as a
  spurious "Request timed out" failure.
- useAutoRun.ts: log unexpected launch failures via webLogger before
  converting them into `{ success: false }` (CodeRabbit). The web bundle
  has no Sentry wired up, so the logger is the equivalent surface.
- mobile/App.tsx: read the pre-launch session state from the current
  sessions snapshot before scheduling the optimistic update instead of
  capturing it as a side effect inside the `setSessions` updater
  (greptile P2). React 18 Concurrent Mode is allowed to re-run updater
  callbacks for speculative/interrupted renders.
- mobile/App.tsx: header indicator now prefers the session-level
  `connecting` state over the active tab's state, and the dot animates
  on both `busy` and `connecting` so the launching agent's pulsing
  orange feedback actually pulses (CodeRabbit).

Note: the remaining four review comments target files introduced by
PR RunMaestro#946 (`useAppRemoteEventListeners.ts`, `worktreeSpawn.ts`,
`AutoRunSetupSheet.tsx`, `AutoRunWorktreeSection.tsx`) and belong with
that PR rather than this Gap 1 follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chr1syy added a commit to chr1syy/Maestro that referenced this pull request May 9, 2026
Closes Gap 3 from the PR RunMaestro#946/RunMaestro#947 mobile/web AutoRun parity follow-up.
Desktop has an "Add Docs" affordance that surfaces both Create-doc and
Marketplace as co-equal entry points. On mobile/web, the empty state of
AutoRunInline only offered "+ Create document" — users with a fresh
AutoRun folder had no in-UI path to discover existing playbooks and had
to know to open the launch sheet just to find the marketplace entry.

- Add onOpenMarketplace?: () => void prop to AutoRunInline. Render a
  "Browse Playbook Exchange" CTA next to "+ Create document" in the
  empty state when the prop is provided. Both buttons share the same
  vertical stack so the discoverability matches desktop's overview.
- Plumb the prop through AutoRunPanel (full-screen overlay),
  AutoRunTabContent / RightDrawer (inline tab), and RightPanel.
- Wire handleOpenMarketplaceSheet through from App.tsx for both
  AutoRunPanel and RightPanel render sites; reuses the same
  MarketplaceSheet and WS infrastructure introduced by PR RunMaestro#947, so no
  new IPC/server surface.

The CTA is opt-in (prop is optional) so existing callers and tests keep
working without change. Existing scoped tests pass; new
AutoRunInline.test.tsx covers the empty-state CTA visibility,
hidden-when-unset, and onClick behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pedramamini
pedramamini merged commit aadb6f7 into RunMaestro:rc May 10, 2026
3 checks passed
chr1syy added a commit to chr1syy/Maestro that referenced this pull request May 10, 2026
pedramamini pushed a commit that referenced this pull request May 10, 2026
- useAutoRun.ts: extend the configure_auto_run timeout to 60s when
  worktree dispatch is enabled (greptile P1). Worktree launches block
  on `git worktree add` plus an upstream getBranches round-trip on the
  server, which routinely exceeds the default 10s and surfaces as a
  spurious "Request timed out" failure.
- useAutoRun.ts: log unexpected launch failures via webLogger before
  converting them into `{ success: false }` (CodeRabbit). The web bundle
  has no Sentry wired up, so the logger is the equivalent surface.
- mobile/App.tsx: read the pre-launch session state from the current
  sessions snapshot before scheduling the optimistic update instead of
  capturing it as a side effect inside the `setSessions` updater
  (greptile P2). React 18 Concurrent Mode is allowed to re-run updater
  callbacks for speculative/interrupted renders.
- mobile/App.tsx: header indicator now prefers the session-level
  `connecting` state over the active tab's state, and the dot animates
  on both `busy` and `connecting` so the launching agent's pulsing
  orange feedback actually pulses (CodeRabbit).

Note: the remaining four review comments target files introduced by
PR #946 (`useAppRemoteEventListeners.ts`, `worktreeSpawn.ts`,
`AutoRunSetupSheet.tsx`, `AutoRunWorktreeSection.tsx`) and belong with
that PR rather than this Gap 1 follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chr1syy added a commit to chr1syy/Maestro that referenced this pull request May 10, 2026
Closes Gap 3 from the PR RunMaestro#946/RunMaestro#947 mobile/web AutoRun parity follow-up.
Desktop has an "Add Docs" affordance that surfaces both Create-doc and
Marketplace as co-equal entry points. On mobile/web, the empty state of
AutoRunInline only offered "+ Create document" — users with a fresh
AutoRun folder had no in-UI path to discover existing playbooks and had
to know to open the launch sheet just to find the marketplace entry.

- Add onOpenMarketplace?: () => void prop to AutoRunInline. Render a
  "Browse Playbook Exchange" CTA next to "+ Create document" in the
  empty state when the prop is provided. Both buttons share the same
  vertical stack so the discoverability matches desktop's overview.
- Plumb the prop through AutoRunPanel (full-screen overlay),
  AutoRunTabContent / RightDrawer (inline tab), and RightPanel.
- Wire handleOpenMarketplaceSheet through from App.tsx for both
  AutoRunPanel and RightPanel render sites; reuses the same
  MarketplaceSheet and WS infrastructure introduced by PR RunMaestro#947, so no
  new IPC/server surface.

The CTA is opt-in (prop is optional) so existing callers and tests keep
working without change. Existing scoped tests pass; new
AutoRunInline.test.tsx covers the empty-state CTA visibility,
hidden-when-unset, and onClick behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
pedramamini pushed a commit that referenced this pull request May 10, 2026
Closes Gap 3 from the PR #946/#947 mobile/web AutoRun parity follow-up.
Desktop has an "Add Docs" affordance that surfaces both Create-doc and
Marketplace as co-equal entry points. On mobile/web, the empty state of
AutoRunInline only offered "+ Create document" — users with a fresh
AutoRun folder had no in-UI path to discover existing playbooks and had
to know to open the launch sheet just to find the marketplace entry.

- Add onOpenMarketplace?: () => void prop to AutoRunInline. Render a
  "Browse Playbook Exchange" CTA next to "+ Create document" in the
  empty state when the prop is provided. Both buttons share the same
  vertical stack so the discoverability matches desktop's overview.
- Plumb the prop through AutoRunPanel (full-screen overlay),
  AutoRunTabContent / RightDrawer (inline tab), and RightPanel.
- Wire handleOpenMarketplaceSheet through from App.tsx for both
  AutoRunPanel and RightPanel render sites; reuses the same
  MarketplaceSheet and WS infrastructure introduced by PR #947, so no
  new IPC/server surface.

The CTA is opt-in (prop is optional) so existing callers and tests keep
working without change. Existing scoped tests pass; new
AutoRunInline.test.tsx covers the empty-state CTA visibility,
hidden-when-unset, and onClick behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chr1syy added a commit that referenced this pull request Jul 11, 2026
…og (#1145)

* fix(autorun): correct worktree attribution + add absolute task watchdog

Two related Auto Run robustness fixes surfaced by a web-desktop launch of a
two-document playbook with auto-worktree creation.

1) Worktree attribution across same-repo agents (PR #946 follow-up)

   - worktreeSpawn: re-mark the resolved worktree path as recently-created
     AFTER worktreeSetup (create-new only). The original 10s dedup TTL started
     before the potentially slow `git worktree add`, so the chokidar discovery
     could fire during the getBranches/buildWorktreeSession window after the
     mark aged out, letting a sibling agent watching the same basePath adopt
     the worktree under the wrong parent. Re-marking also covers the
     existingPath reassignment the original mark never did.
   - useWorktreeHandlers: scope onWorktreeDiscovered dedup per-parent instead
     of globally by cwd. Each same-repo agent runs its own watcher and fires
     its own discovery event, so an externally-created worktree now fans out:
     every agent in the cwd gets its own child instead of the first watcher
     claiming it globally and the others silently dropping it. Maestro-spawned
     worktrees still attach only to the launching agent (the dedup mark
     suppresses sibling watchers).

2) Absolute Auto Run task watchdog

   The per-document batch loop awaits processTask, whose only recovery was the
   silence-based inactivity watchdog. A stuck-but-chatty agent that keeps
   emitting output (resetting the silence timer) but never finishes defeats it,
   hanging the whole multi-document run with no advance to the next document.
   - Add an absolute wall-clock per-task cap (autoRunMaxTaskDurationMin,
     default 480 min, 0 = unlimited) that force-kills regardless of output and
     resolves the task as watchdog-timeout, which the batch loop already
     handles by terminating the document and moving on.
   - Wire the new setting through metadata, store, useSettings, the General
     tab UI (alongside the inactivity timeout), and searchable settings.

Tests: worktree attribution (per-parent fan-out, mark-skip, idempotency,
cross-repo reject), worktreeSpawn re-mark robustness, and batch watchdog
(absolute cap fires on chatty-stuck, inactivity still fires on silence, both
unlimited = no watchdog). Updated the existing mark-order assertion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(autorun): address review feedback on worktree attribution + watchdog

Scope worktree reuse/dedup to the launching parent and harden the Auto Run
max-task-duration cap, per PR #1145 review threads.

- worktreeSpawn: reuse an existing worktree session only when it is OWNED by the
  launching parent (new sessionOwnedByParent helper). A global cwd match let a
  same-repo sibling's child be adopted for this parent's Auto Run, reproducing
  the wrong-parent attribution the flow exists to prevent (codex + greptile P1).
- useWorktreeHandlers rescan: apply the same per-parent dedup that the chokidar
  path already uses, so a restart/visibility rescan fans a shared worktree out
  to every same-repo parent instead of letting the first to iterate claim it
  globally (codex P2).
- settingsStore: sanitize the persisted autoRunMaxTaskDurationMin on load so a
  corrupt (non-finite/negative/wrong-type) value can no longer silently DISABLE
  the cap; extract clampAutoRunMaxTaskDurationMin so setter and loader agree
  (greptile P2).
- settingsStore: migrate existing "Unlimited" inactivity installs (persisted 0
  with no cap set) to an unlimited absolute cap (0) so the new 480-min default
  doesn't silently start killing previously-uncapped runs (codex P2).
- Replace prohibited em/en dashes introduced by this PR in copy, comments, and
  tests per the repo style rule (codex P2).

Adds tests: parent-scoped worktree reuse, rescan per-parent fan-out, and the
watchdog sanitize/clamp/migration helpers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(autorun): tighten worktree ownership + rescan/dedup-mark robustness

Follow-up review on the worktree-attribution fix:

- sessionOwnedByParent: match ownership by parentSessionId ONLY. The legacy
  worktreeParentPath fallback compared against the parent's cwd, but that field
  is the scanned worktree BASE directory, which several same-repo parents can
  share - so it could match the wrong owner. Legacy sessions now fall through to
  getting a fresh new-model child for the launcher, which is safe (no
  misattribution) even if the legacy child isn't reused.
- scanWorktreeConfigs: skip a subdir whose path is still marked recently-created
  by spawnWorktreeAgentAndDispatch (mirrors the chokidar listener). Without it, a
  startup/visibility rescan inside the spawn window would fan a Maestro-launched
  worktree out under every same-repo parent - and the rescan is the ONLY
  discovery path on SSH, where chokidar is unavailable.
- worktreeSpawn: give the pre-`git worktree add` dedup mark a 60s TTL so it can't
  expire mid-setup on a large repo / cold disk / SSH remote (the default 10s
  window could lapse before the post-setup re-mark). The error/already-existed
  branches clear the path and the success path re-marks with the default TTL, so
  the long window never outlives the setup it guards.

Tests: rescan skips a recently-created marked path; existing parent-scoped reuse
and fan-out tests still pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(autorun): concurrency + migration + copy fixes from follow-up review

- worktreeSpawn: refuse to build a second child into a worktree a BUSY sibling
  is already running in. worktreeSetup can resolve to a branch attached under a
  sibling agent; reuse stays parent-scoped, but dispatching a fresh child into
  that same checkout would let two agents write it concurrently. Bail if any
  same-root session is busy/connecting.
- worktreeSpawn: re-mark the resolved path with the long setup TTL (not the
  default 10s). The owning session isn't committed until after the awaited
  getBranches, which is slow on SSH/large repos, so the mark must survive that
  window too.
- settingsStore: make the "Unlimited inactivity" -> unlimited-cap migration
  one-shot by persisting the migrated value. Without writing the key back, the
  branch re-ran on every load and would silently reset a cap the user set later.
- useWorktreeHandlers.handleSaveWorktreeConfig: scope the save-time scan's
  existing-child checks to the active parent (and skip recently-created marks),
  mirroring scanWorktreeConfigs. A global cwd match let a second same-repo agent
  enabling the same basePath be skipped, leaving it childless until a later
  rescan.
- Clarify the inactivity "Unlimited" copy: it disables the inactivity watchdog
  only; the Max Task Duration cap still applies unless also set to Unlimited.

Tests: busy same-root sibling is rejected (no duplicate child); the unlimited
migration persists its value one-shot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(autorun): expect the 60s setup TTL on the worktree dedup re-mark

Follows the re-mark TTL change: the worktree Auto Run test asserted
markWorktreePathAsRecentlyCreated was called with only the path, but both marks
now pass WORKTREE_SETUP_MARK_TTL_MS (60000).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready to merge This PR is ready to merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants