Skip to content

Worktree creation: nests under active worktree-session cwd; manual path silently aborts on ENOENT #1399

Description

@mislaughter1989-del

Bug: Worktree creation button uses active-session cwd instead of repo toplevel, and manual path entry silently aborts on ENOENT

Summary

Two related bugs in the mobile app's worktree-creation flow on Happy CLI v1.1.8:

  1. Nesting bug: When the user invokes "+ new worktree" from a session whose cwd is itself a worktree, the new worktree is created nested under that worktree instead of at the repo toplevel. The on-disk result is a confusing repo/.dev/worktree/<active>/.dev/worktree/<new> path. Git accepts it (all worktrees share .git) but the path is junk from a UX standpoint.

  2. Manual path silent-abort: When the user types a path manually for a new repo, the phone pre-probes the path with git worktree list --porcelain. If the path doesn't exist, the daemon returns spawn /bin/sh ENOENT and the phone bails silently. The daemon's existing requestToApproveDirectoryCreation (HTTP 409 → actionRequired: "CREATE_DIRECTORY") flow is never used.

Environment

  • Happy CLI: 1.1.8
  • Daemon mode: start-sync, healthy
  • OS: Linux (Ubuntu, kernel 6.17)
  • Daemon log: ~/.happy/logs/2026-06-14-07-35-34-pid-265297-daemon.log

Reproduction — Nesting bug

  1. From the mobile app, open a session whose cwd is an existing worktree (e.g. 01_Server/.dev/worktree/bold-ocean).
  2. Tap "+ new worktree from selected repo".
  3. The phone fires (in order):
[11:03:09.781] Shell command request: git worktree add -b eager-star .dev/worktree/eager-star
[11:03:09.962] Shell command result: { success: true, exitCode: 0, ... }

[11:03:10.234] [API MACHINE] Received RPC: spawn-happy-session
[11:03:10.236] params: {
  "directory": "/home/michael/01_Server/.dev/worktree/bold-ocean/.dev/worktree/eager-star",
  "agent": "claude"
}
[11:03:10.254] [SPAWN HAPPY CLI] Spawning: happy claude ... in
   /home/michael/01_Server/.dev/worktree/bold-ocean/.dev/worktree/eager-star

Expected on-disk location: 01_Server/.dev/worktree/eager-star
Actual: 01_Server/.dev/worktree/bold-ocean/.dev/worktree/eager-star

git worktree list confirms:

/home/michael/01_Server/.dev/worktree/bold-ocean/.dev/worktree/eager-star  eb7b4fd [eager-star]

For contrast, when invoked from a session whose cwd is the main repo toplevel the path is correct:

[11:03:56.886] Shell command request: git worktree add -b smooth-mountain .dev/worktree/smooth-mountain
[11:03:57.317] Spawning: ... directory: /home/michael/01_Server/.dev/worktree/smooth-mountain

Reproduction — Manual path silent-abort

  1. From the new-session screen, type a repo path that does not yet exist on disk.
  2. Phone fires git worktree list --porcelain with cwd = <typed path>.
  3. Daemon returns:
[11:06:13.155] Shell command failed: {
  success: false,
  exitCode: 1,
  error: 'spawn /bin/sh ENOENT',
  stdoutLen: 0, stderrLen: 20
}

(Repeats while the user keeps typing.)
4. Phone never advances to spawn-happy-session, never shows a "create directory?" prompt. UI just appears unresponsive.

The daemon already supports a clean create-on-spawn path: spawn-happy-session with approvedNewDirectoryCreation: false returns HTTP 409 + actionRequired: "CREATE_DIRECTORY", expecting the client to prompt and re-send with approvedNewDirectoryCreation: true. That flow is bypassed here.

Root cause

The phone uses the active session's cwd as the base for the git worktree add shell command. For sessions whose cwd is itself a worktree, the relative path .dev/worktree/<new> resolves under that worktree instead of the repo toplevel.

For manual-path entry, the phone's pre-flight git worktree list --porcelain probe fails ENOENT on non-existent dirs and there's no fallback to the daemon's existing create-approval flow.

Suggested fix

Nesting bug

Before sending the git worktree add shell command, resolve the repo toplevel:

git -C <selected-session-cwd> rev-parse --show-toplevel
# or, to also handle the worktree case explicitly:
git -C <selected-session-cwd> rev-parse --path-format=absolute --git-common-dir
# common-dir's parent is the main checkout

Then either:

  • cd <toplevel> for the git worktree add (cwd = toplevel)
  • or pass an absolute path: git worktree add -b <name> <toplevel>/.dev/worktree/<name>

Worktrees should live as siblings at the repo toplevel, never nested. Git tracks them flat under .git/worktrees/ regardless of disk layout; nesting only confuses humans and tools.

Manual path silent-abort

Skip the pre-flight git worktree list --porcelain probe (or treat ENOENT as "path doesn't exist yet" instead of error). Then go straight to spawn-happy-session with approvedNewDirectoryCreation: false and surface the daemon's HTTP 409 response as a "Create dir + new worktree?" UI prompt. The daemon already does the mkdir -p on confirmation.

Bonus UX

Show the resolved target path in the UI before confirming, so the user sees 01_Server/.dev/worktree/eager-river rather than just a name. Currently the target is invisible until the session opens at a possibly-surprising path.

Branch source (open question)

Currently git worktree add -b <name> <path> with no source branches from the cwd's HEAD. This is reasonable when invoked from a feature worktree (continue the branch). When invoked from main, branches from main. Worth surfacing in UI; or default to origin/<default-branch> matching the Claude Agent SDK's worktree.branchFrom: "fresh".

Files referenced

  • Daemon RPC spawn-session: dist/index-q9G4ktSK.mjs:4102-4161
  • Daemon spawnSession impl (mkdir + approval flow): dist/index-q9G4ktSK.mjs:5214-5253
  • Daemon bash RPC handler: dist/types-CIyZti-h.mjs:872-939
  • Reproduction logs: ~/.happy/logs/2026-06-14-07-35-34-pid-265297-daemon.log (lines 11:03:09 — 11:06:16)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions