Skip to content

🐛 trajectory.db gets committed to scratch project + leaks into worktrees (no .gitignore) #171

@ZaxShen

Description

@ZaxShen

Surfaced by local h5 test run (#169 follow-up). Bro flagged it from inside the session.

What happened

Bro `cd`'d into a worktree to run V2 verification verbatim. From inside the worktree, every subsequent `git branch -m` or other Bash call got blocked by the `git-guards.sh` PreToolUse hook — because `tmb_db_path` resolved to the worktree's stale, empty trajectory.db (which lacks the `plugin_config` policy keys), and git-guards treats missing keys as deny-by-default.

Bro's quote:

"I cd'd into the worktree to run V2 verification verbatim — that left CC's bash pwd inside the worktree, where a stale `.claude/tmb/trajectory.db` (a tracked file copied at worktree creation) doesn't have the plugin_config keys. Every subsequent `git branch -m` call gets blocked by the git-guards PreToolUse hook"

Root cause

Two compounding problems:

1. The scratch project (and any user's fresh project) has no `.gitignore` for `.claude/`

`tests/dogfood/lib/flow-helpers.sh:l5_setup_scratch_project`:
```bash
git init -q -b main
echo "init" > README.md
git add . && git commit -qm init # ← no .gitignore
mkdir -p .claude/tmb # ← .claude is NOT ignored
```

`l5_setup_scenario_state` then runs `git add . && git commit -qm "scenario setup files"`, which commits the trajectory.db that was just created. `git worktree add` checks out the committed tree → worktree contains a stale copy of the DB.

This affects real users too, not just the test framework. When bro activates in a fresh user project, nothing ensures `.gitignore` excludes `.claude/`. Same DB-leak-into-worktree footgun applies.

2. `tmb_db_path` falls back to `$(pwd)/.claude/tmb/trajectory.db` without walking up to git root

`scripts/hooks/lib/query-task.sh:tmb_db_path`:
```bash
p="$(pwd)/.claude/tmb/trajectory.db"
[ -f "$p" ] && echo "$p"
```

When pwd is inside a worktree at `/.claude/worktrees//`, this finds the stale worktree-local DB instead of the project-root DB. Hooks that depend on policy keys (git-guards) then fail-deny because keys appear missing.

The fix

Two-part:

Test framework (immediate): `l5_setup_scratch_project` should write `.gitignore` containing `.claude/` BEFORE the initial commit.

Plugin (real-user impact): First-run onboarding should ensure the user's project `.gitignore` includes `.claude/`. Either:

  • The activation-routine hook (or a SessionStart hook) appends `.claude/` to `.gitignore` if missing.
  • Or `tmb_first-run-onboarding` skill writes it.

Hook robustness (defense-in-depth): `tmb_db_path` should walk up to the git root and use `/.claude//trajectory.db`, OR honor an env var that the plugin sets at session start. Worktree-local DB lookups should never silently win.

SWE doctrine: Bro's verification step (in `tmb_validate-swe-output`) should use `git -C ` rather than `cd ` to keep pwd stable. Once trapped inside the worktree, every PreToolUse hook misfires.

Repro

Run any code-touching prompt that spawns SWE in a fresh scratch project. Inspect the worktree:

```
ls .claude/worktrees//.claude/tmb/trajectory.db # exists, stale
```

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't workingTestsTest infrastructure (L0-L6)WorkflowBro / SWE / pr-reviewer doctrine + planning skills

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions