Skip to content

bug(agents): verifier preparation rejects every Git worktree, so worktree-based ODD work cannot be independently verified #3231

Open

Description

@jonasotoaguilar

Before submitting

  • I searched open and closed issues and did not find a report of this problem.
  • I reviewed this report and removed credentials, tokens, private paths, hostnames, and other sensitive data.

Problem

gentle-ai-verify cannot verify work performed in a Git worktree. Its preparation helper scripts/prepare-verifier.mjs rejects any directory that is not an ordinary single-root checkout, so an admitted verifier aborts with exit 1 before running a single probe.

This is deterministic and platform-independent: it is a directory-shape check, not a copy, timeout or artifact problem. It hits the workflow the harness itself prescribes — AGENTS.md requires tracked ODD work to live in <repo-parent>/<repo-name>-worktrees/<worktree-name> — so independent verification is unavailable precisely where ODD work is supposed to happen, and the returned verdict reads like a candidate failure instead of a setup blocker.

Related but distinct: #3009 (isolated preparation reliability: copy, sockets, timeout) and #2986 (Windows whole-worktree copy timeout). Those are about the copy step; this report is about the guard that refuses the source directory before any copy is attempted.

Cause: scripts/prepare-verifier.mjs requires .git to be a real directory and git rev-parse --git-common-dir to resolve to <source>/.git. A linked worktree fails both by construction: .git is a file containing gitdir: …, and --git-common-dir resolves to the main repository's .git, which is the shared object store. The helper is injected for this role only (lib/agents-runner.ts, GENTLE_PI_VERIFIER_PREPARER).

What the guard actually needs is isolation, not checkout shape: not shallow, no objects/info/alternates, no sparse/partial/replace, a stable index, and the worktree contained inside the source. All of those are satisfiable by a linked worktree.

Possible fix:

  1. Accept either shape: .git as a directory, or .git as a regular file whose gitdir: target resolves under the same common dir (<common>/.git/worktrees/<name>); keep --show-toplevel equal to the source, and keep every existing rejection (shallow, alternates, sparse, partial, replace, index flags) unchanged.
  2. Cover it in tests/verifier-preparation.test.ts with a real git worktree add fixture, asserting preparation succeeds and that a shallow or alternates worktree is still refused.
  3. Cheaper alternative if the isolation argument is not accepted: detect a worktree source in the runner and fail the delegation immediately with an actionable message ("verification requires a non-worktree checkout"), instead of letting the subagent spend turns and then emit a FAIL that is indistinguishable from a defect verdict.
  4. Until then, docs/delegated-verification.md should state that worktrees are unsupported and how to work around it (disposable clone).

Steps to reproduce

  1. Create a worktree for a repository, exactly as the harness documentation prescribes:
    git worktree add ../<repo>-worktrees/feature -b feat/example main
  2. Delegate verification with that worktree as the workspace, e.g. subagent_run with agent: "gentle-ai-verify" and workspace_root: "<path>/<repo>-worktrees/feature". Calling the helper directly shows the same thing:
    s=$(mktemp -d) && node "$GENTLE_PI_VERIFIER_PREPARER" "$PWD" "$s" '{"include":[],"checks":["node --test tools/*.test.js"]}'
    run from inside the worktree.
  3. Observe exit 1 with unsupported repository: ordinary single-root Git directory required, and zero probes executed.

Control: the identical candidate in an ordinary clone at the same commit prepares successfully and runs the checks. Observed on Linux with gentle-pi 4.7.0 and Pi 1.5.2, worktree created from Gentleman-Programming/gentle-shell main.

Expected and actual behavior

Expected: the verifier prepares the source it was pointed at (HEAD, index and tracked bytes of a worktree are self-contained), or the runner refuses that delegation up front with an actionable message, before a subagent spends turns on it. A setup failure must be reported as a setup failure, never as a verification verdict.

Actual: preparation exits 1, no check runs, and the only signal is the prose paragraph in the verifier's report; the summary line still reads FAIL. The parent has to infer that the code was never examined.

gentle-pi version

4.7.0

Pi version

1.5.2

Operating system

Linux

Relevant logs or error output (optional)

The delegated verifier ran the documented preparation command and got:

exit 1
unsupported repository: ordinary single-root Git directory required

The guard, from scripts/prepare-verifier.mjs:

if (!lstatSync(join(source, ".git")).isDirectory() || lstatSync(join(source, ".git")).isSymbolicLink() ||
	realpathSync(git(source, ["rev-parse", "--show-toplevel"]).trim()) !== source ||
	resolve(source, git(source, ["rev-parse", "--git-common-dir"]).trim()) !== join(source, ".git")) fail("unsupported repository: ordinary single-root Git directory required");

In a linked worktree, git rev-parse --git-common-dir prints the main repository's .git path, and .git is a file, so this condition always fails.

Injection point, from lib/agents-runner.ts:

if (request.agent.name === "gentle-ai-verify") env.GENTLE_PI_VERIFIER_PREPARER = fileURLToPath(new URL("../scripts/prepare-verifier.mjs", import.meta.url));

Activity

  1. jonasotoaguilar commented on Nov 14, 2026

    @jonasotoaguilar
    ContributorAuthor

    Additional reproduction in a separate project: a gentle-ai-verify delegation against a linked worktree with uncommitted changes failed during the mandatory Git preparation step with unsupported repository: ordinary single-root Git directory required (exit 1). The verifier reported probesRun: 0, so neither the candidate nor baseline checks ran. It could only provide a read-only structural review; the required independent functional verification remained unavailable and blocked delivery. This is the same worktree restriction described above, not evidence of a candidate test failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions