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
- 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.
- 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.
pilot worktree create/syncbuild the integration-lock path from the shell's cwd, not the repository rootVersion: Pilot Shell v10.2.4 (Solo tier), Linux, Python 3.12 build.
Summary
pilot worktree createandpilot worktree syncfail when the shell's working directory is asubdirectory of the repository rather than its root. They construct the repository integration lock
path as
<cwd>/.git/pilot-sync.lockinstead of<repo root>/.git/pilot-sync.lock, then report theresulting
ENOENTas 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 thatreliably sends an agent down the wrong recovery path.
Reproduction
From a subdirectory of a git repository:
pilot worktree syncfails identically, with exit code 1 andfiles_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:
So repo-root resolution is not missing from the codebase — it is simply not used when composing the
integration-lock path.
createandsyncappear to be the only two subcommands that take this lock,and therefore the only two affected.
The intended location is confirmable: after a successful
syncrun from the repository root, thelock exists at
<repo root>/.git/pilot-sync.lockand nowhere else.Impact, and why the wording matters more than the failure
syncis the single irreversible step in the lane workflow, and it runs after the merge-approvalgate 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: falsegets no signal at all about the cause.Suggested fix
integration-lock path, consistent with what
detect/status/review-scopealready do.opened at
<path>" is a different problem from "another run holds the lock", and only the secondshould suggest waiting.
Workaround
Prefix every invocation with an explicit working directory:
This is reliable but has to be remembered per call, and nothing in the failure text points toward it.