skip to content
Jobs

Finding the Real Checkout Behind a Worktree

Making run identity resolve to the primary checkout no matter where Gantry is invoked

Gantry milestones
Making run identity resolve to the primary checkout no matter where Gantry is invoked

Every Gantry run keeps its work in its own git worktree, and Gantry finds that worktree again — to show status, enter it, merge it, clean it up — by deriving a path from the plan name and the repository root. That derivation assumed you were standing in the main checkout. Git lets you attach extra working directories to one repository, called linked worktrees, and when Gantry was invoked from inside one, it read the linked worktree as the repository root. The paths it computed came out wrong: a run started from one place was no longer addressable from another, and cleanup and lookup could point at directories that had a doubled worktree-storage segment or carried the linked worktree's own name.

This job fixed the resolution at its source. It taught Gantry to ask git for the shared root that the primary checkout and all of its linked worktrees agree on, so run identity anchors to the same place regardless of where the command was run — while keeping a separate, deliberate answer for the callers that really do need the checkout you are currently standing in.

Build

The work split into fixing the shared root resolver first, then auditing every run-management entrypoint against it — a root-cause-before-callers order that let the invariant be proven before any command leaned on it.

The decomposition put the root cause first and the callers second. The opening sprint changed one thing: how the repository root is resolved at the shared path-resolution boundary, so that resolving the same repository from the primary checkout or from a linked worktree yields the same primary-checkout root. Its done-condition was an invariant, not a feature — the derived run worktree path must be identical either way, with no doubled worktree-storage segment. That is a property one sprint could establish and test in isolation, before any status or merge or cleanup command was touched.

Only then did the second sprint carry that root through the command layer, auditing status, enter, merge, clean, remove, launch, resume, detach, and the assistant-facing run views so each one identifies a run from the primary-checkout root rather than from whatever directory it happened to be invoked in. The order matters: with the resolver already correct and covered, the audit is a mechanical check of who calls it, not a hunt for the bug and its callers at once.

The load-bearing distinction the second sprint drew is between two needs that had been conflated. Run identity wants the one root every invocation agrees on. But a caller that reads and writes a checkout's own files wants the opposite — the checkout it is actually standing in — because rooting that work at the primary checkout would send its writes outside the current worktree. The audit's brief made that explicit: callers that intentionally operate on the current working tree must stay deliberate. Rather than redirect every path to the primary root, the sprint produced a second, separately named resolver for that case, so the exception is stated in the code rather than inherited by accident.

The run never went red. No gate sent a sprint back, no review forced a re-plan, and nothing had to be retried. That is the signature of a cut that dissolved the difficulty in advance: isolating the resolver fix as its own provable step meant the harder, wider caller audit inherited a foundation that already held, leaving it with no ambiguity left to trip on.

Feature

Gantry now resolves the primary checkout by climbing git's shared common directory, with an explicit second resolver for callers that need the current worktree — and both, plus their reasoning, are still in the tree at HEAD.

Before this job, invoking Gantry from a linked git worktree corrupted run identity. The path derivation treated the linked worktree as the repository root, so lookups and cleanup could synthesize phantom directories — nested worktree storage, or a run name carrying the linked worktree's basename — and a run addressable from one location was invisible from another.

The answer is a pair of resolvers with clearly opposed jobs. One climbs to the primary checkout: it asks git for the shared common directory, which every linked worktree and the primary checkout point at, and returns that directory's parent — the single root all invocations agree on. It handles the awkward case too, where a primary checkout keeps its git directory elsewhere, and falls back to the working-tree top there. The other resolver returns the checkout you are actually in, for the callers that must read and write that tree's own files. The common wrapper used across the command layer routes through the primary-checkout resolver, so run identity and the per-plan orchestration directory anchor consistently.

Standing in the tree at today's HEAD, the design is intact. Both resolvers are present in the worktree module, and the comment blocks that explain why one climbs and the other does not — including the sandbox's read-only bind of the main checkout as the reason writes must stay in the current worktree — are still there verbatim. The primary-checkout wrapper is called from detach, the TUI, preflight, the run registry, the run lock, the chat engine, top-level command dispatch, and the build engine; the current-checkout resolver appears exactly at the deliberate exception the audit carved out. All of the files the job touched survive, most of the lines it introduced are still in place, and the source plan document it referenced was tidied away once the work landed. Nothing since has folded the distinction back into a single root.

6 sessions
peak 124,006 context
unavailable tokens
unavailable cost
0 x 2 milestones x sprints
12 edits
130 commands
31m duration
2 execute · 2 review · 2 plan · 1 gate-build roles
0 x 0 fixes x replans
codex harness