detect_root: a parent's .code-index.toml captures nested git worktrees/clones — agent worktree sessions silently answer from the wrong tree #553
Labels
No labels
code-review
correctness
dos
performance
security
severity/high
severity/low
severity/medium
tech-debt
Kind/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Blocks
Reference
h-dv/code-index#553
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
detect_rootlets a.code-index.tomlin a PARENT directory capture a nested git repository or worktree. Every agent session started inside a git worktree under the main checkout (.claude/worktrees/<lane>/) silently indexes and answers from the parent tree instead of its own. HEAD is usually identical, so the answers look right, but the agent's own edits are invisible to every tool.Field evidence (2026-11-09, dogfood across 5 sessions)
.claude/worktrees/lane-a-binds:read_codeon its own files →path_not_indexed(hidden.claudedir);project_overviewshowed the parent'suncommitted_changes: truewhile the lane was clean.codex-512,codex-546:code-index-mcpran with cwd = the worktree (verified via/proc/<pid>/cwd) and served/home/master/code/rust/cosi-mcp.read_codeon their own edited files →path_not_indexed;changed_symbolscould not see their diffs.codex-534:index_coverage(<own file>)→verdict: never, reason: hidden.Cause (verified by reading the code)
crates/indexer/src/project.rsdetect_root_within: tier 1 (.code-index.toml, innermost) walks ALL ancestors before tier 2 (.git) runs. The primary checkout had an untracked.code-index.toml, so a worktree whose only marker is its own.gitfile resolves to the parent. The same applies to a nested clone or submodule (.gitdirectory) under any directory with a.code-index.toml.Fix (in progress, branch
codex-t1)One upward walk. At each ancestor, check
.code-index.tomland then.git(file or directory). The first (innermost) hit of either wins..code-index.tomlinside the repository still overrides the repository root, which keeps the monorepo sub-project semantics..code-index.tomlabove the innermost.gitboundary never captures that repository.$HOME) is unchanged.Acceptance
.gitfile) and nested clone (.gitdir) under a dir with.code-index.toml→ root is the nested repo; in-repo.code-index.tomlstill wins; outer toml + same-dir.gitstill wins..gitbefore the toml in the single walk (in-repo override goes red)..code-index.tomlfrom the main checkout (operator-approved).Related: #521 (worktree gaps 1–2; this was the actual cause of the lane-A field case recorded there).
Fixed on branch
codex-t1(835af751), merged into the integration branchintegration-1109(e5471419). It lands on master with the next integrated train..code-index.tomland then.git(file or dir); the innermost hit wins. Tier 3 is unchanged.worktree_git_file_beats_outer_config,nested_git_dir_beats_outer_config,inner_config_overrides_git,config_and_git_at_root_return_root..gitbefore the toml in the single walk) cannot fail. At one ancestor both checks return the same path. The worker showed that and used other mutations instead..code-index.tomlin the main checkout was removed (backed up), as the operator approved.Released since v0.41.48 (
835af751). Checked again on master3d0a584c(v0.41.50): the single upward walk and its tests (worktree_git_file_beats_outer_config,nested_git_dir_beats_outer_configincrates/indexer/src/project.rs) are in the tree and run in CI. Closing.