Skip to content

[BUG] Worktree provisioning writes tracked hook config to shared info/exclude #392

Description

@StirlingGoetz

Description

Summary

With scm.isolation = "worktree" and the Codex adapter, worktree provisioning adds /.codex/hooks.json to the repository's shared .git/info/exclude, even when .codex/hooks.json is already tracked and was not modified during provisioning.

This makes the tracked hook configuration appear ignored and can break repository-hygiene or pre-commit checks.

Environment

  • bmad-loop: 0.9.0
  • Installed source revision: 232f3f7159004e960e786a6568ebe79daecf388d
  • Upstream main also affected at: ad22f7966b0d0626bea3a22f0617f55c875b7810
  • Adapter: Codex
  • Git: 2.54.0
  • OS: Linux
  • SCM policy:
    • isolation = "worktree"
    • branch_per = "story"
    • seed_adapter_defaults = true

Steps to reproduce

Steps to reproduce

  1. Create a Git repository with a tracked .codex/hooks.json:

    git add .codex/hooks.json
    git commit -m "Track Codex hook configuration"Confirm no tracked files are ignored:

git ls-files -ci --exclude-standard

Configure bmad-loop to use the Codex adapter and worktree isolation.

Start a run that provisions an isolated story worktree.

Inspect the common Git exclude file:
git check-ignore --no-index -v .codex/hooks.json

Check for tracked-and-ignored files again:
git ls-files -ci --exclude-standard

Expected behavior

Expected behavior
Worktree provisioning should:
Never make an already-tracked file ignored.
Avoid changing ignore behavior for the main checkout or unrelated worktrees.
Exclude only untracked files actually created by provisioning.
Remove any provisioning-owned metadata during worktree/run cleanup.

Actual behavior

Actual behavior
After provisioning:
.git/info/exclude:...:/.codex/hooks.json .codex/hooks.json
and:
.codex/hooks.json
is returned by git ls-files -ci --exclude-standard.
provision_worktree() unconditionally adds every non-hookless profile's hook configuration to the exclusion set:
patterns |= {
f"/{p.hooks.config_path}"
for p in profiles
if not p.hookless
}
_worktree_local_exclude() then resolves git rev-parse --git-common-dir and writes to:
common_dir / "info" / "exclude"
For a linked worktree, that is the main repository's shared Git directory. The exclusion therefore affects the main checkout and every linked worktree; it is not worktree-local.
Deleting the run or its worktree does not remove the shared exclusion entry.

Impact
Repositories that reject tracked-and-ignored files can pass preflight and then begin failing immediately after bmad-loop provisions its story worktree.
In our run, this blocked the repository commit/review gate and resulted in a CRITICAL review escalation. The implementation remained isolated and was never merged, but the shared .git/info/exclude residue required manual cleanup.
Suggested fix
Before adding a path to the exclusion set, check whether it is tracked:
git ls-files --error-unmatch --
Never exclude tracked paths.

Track which hook or skill paths were actually created or modified during provisioning; do not add every configured path unconditionally.

Do not describe or implement /info/exclude as worktree-local. Use genuinely worktree-scoped state where possible, or explicitly manage shared entries with ownership and cleanup.

Add a regression test that:
Starts with a tracked .codex/hooks.json.
Provisions a linked worktree.
Asserts git ls-files -ci --exclude-standard remains empty in both the main checkout and linked worktree.
Deletes the run/worktree and verifies no exclude residue remains.

Temporary workaround
After worktree provisioning, remove /.codex/hooks.json from the common repository's .git/info/exclude.
Removing it before the run is insufficient because provisioning adds it again.

The upstream issue tracker currently reports

Screenshots

No response

Which area is this for?

Orchestrator / control loop

bmad-loop Version

0.9.0

Which coding CLI are you using?

Codex (codex)

Operating System

Linux

Relevant log output

codex-cli 0.144.6

Diagnostic dump (bmad-loop diagnose)

PAUSED: CRITICAL escalation from review session: ### Status

  • Final status: blocked.
  • Blocking condition: the required scoped checkpoint cannot commit because bun run check:repo-hygiene rejects the pre-existing tracked-and-ignored .codex/hooks.json; bypassing the repository hook or changing that out-of-scope file was not authorized.
  • Secret scan: PASS for the five scoped review files.

Confirm

  • I've searched for existing issues
  • If I attached a diagnose dump, I reviewed it and confirmed it contains no PII, secrets, or project-specific information
  • I'm using the latest version

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions