Skip to content

pilot worktree create/sync build the integration-lock path from the shell's cwd, not the repository root #179

Description

@buddhamangler

pilot worktree create / sync build the integration-lock path from the shell's cwd, not the repository root

Version: Pilot Shell v10.2.4 (Solo tier), Linux, Python 3.12 build.

Summary

pilot worktree create and pilot worktree sync fail when the shell's working directory is a
subdirectory of the repository rather than its root. They construct the repository integration lock
path as <cwd>/.git/pilot-sync.lock instead of <repo root>/.git/pilot-sync.lock, then report the
resulting ENOENT as a serialization refusal rather than a path error.

The failure is safe — nothing is modified, files_changed: 0 — but it is misreported in a way that
reliably sends an agent down the wrong recovery path.

Reproduction

From a subdirectory of a git repository:

$ cd <repo root>/some/nested/dir
$ pilot worktree create --json my-slug --lane my-lane
{"success": false, "error": "Cannot open the repository integration lock
 (<repo root>/some/nested/dir/.git/pilot-sync.lock):
 [Errno 2] No such file or directory: ... Refusing to integrate a lane run unserialized."}

pilot worktree sync fails identically, with exit code 1 and files_changed: 0.

Both succeed immediately when the same command is re-run from the repository root.

Why this looks like a straightforward fix

The same binary already resolves the repository correctly elsewhere. Run from that same nested
subdirectory, these all succeed and return correct results:

$ pilot worktree detect --json some-slug     -> {"found": false}
$ pilot worktree status --json               -> {"active": false}
$ pilot review-scope --json                  -> {"mode": "working-tree", "base_ref": "HEAD", ...}

So repo-root resolution is not missing from the codebase — it is simply not used when composing the
integration-lock path. create and sync appear to be the only two subcommands that take this lock,
and therefore the only two affected.

The intended location is confirmable: after a successful sync run from the repository root, the
lock exists at <repo root>/.git/pilot-sync.lock and nowhere else.

Impact, and why the wording matters more than the failure

sync is the single irreversible step in the lane workflow, and it runs after the merge-approval
gate has already passed. At that moment the operator or coordinating agent is looking for a reason
the merge did not land.

The message says "Refusing to integrate a lane run unserialized." That phrasing describes lock
contention — another lane holding the lock — and the documented response to genuine contention is to
wait and retry. An agent that believes the wording therefore retries a path bug indefinitely, and the
retry can never succeed because the working directory never changes.

A caller that reads only success: false gets no signal at all about the cause.

Suggested fix

  1. Resolve the repository root by walking up to the enclosing git repository when composing the
    integration-lock path, consistent with what detect / status / review-scope already do.
  2. Separate the two failure conditions in the error text. "The integration lock file could not be
    opened at <path>" is a different problem from "another run holds the lock", and only the second
    should suggest waiting.

Workaround

Prefix every invocation with an explicit working directory:

env -C <repo root> pilot worktree create ...
env -C <repo root> pilot worktree sync ...

This is reliable but has to be remembered per call, and nothing in the failure text points toward it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions