BOHICA-LABS / BOHICA-LABS/vsdd-factory
bug(factory-health): 'git worktree add .factory factory-artifacts' silently mounts at .factory/.factory/ when racing with dispatcher
- Dominant language
- Rust
- Stars
- 2
- Forks
- 1
- Avg merge
- 6h 43m
- Merged PRs (30d)
- 29
Description
## Summary
When `/vsdd-factory:factory-health` runs `git worktree add .factory factory-artifacts` against a project where the dispatcher is concurrently recreating `.factory/logs/`, the worktree silently ends up mounted at `.factory/.factory/` (one level too deep) instead of `.factory/`. Git reports success; the skill's downstream validations do not detect the nested mount.
This is **possibly related to #130** (which fixed the recursive shadow caused by dispatcher cwd resolution), but the symptom here is different: there is no `.factory/.factory/logs/` shadow — the *worktree itself* is one level too deep.
## Repro
1. Clean project, `factory-artifacts` orphan branch exists.
2. Trigger the dispatcher to create `.factory/logs/` (e.g., via `factory-obs register` + any hook firing).
3. Move `.factory/` aside (`mv .factory .factory.bak`).
4. Run `git worktree add .factory factory-artifacts`.
5. Inspect:
- `git worktree list` reports `/project/.factory/.factory [factory-artifacts]`
- `.factory/.git` points to `/project/.git/worktrees/-factory/`
- But `.factory/` actually contains a nested `.factory/` worktree, not the worktree root.
In my run, `git worktree add .factory factory-artifacts` printed:
```
Preparing worktree (checking out 'factory-artifacts')
HEAD is now at 0bb23d7 chore: initialize factory-artifacts orphan branch
```
— a clean success. But the resulting layout was:
```
.factory/ ← plain dir (recreated by dispatcher between mv and add)
.factory/.factory/ ← the actual worktree
.factory/logs/ ← dispatcher logs from the race window
```
## Why this slips past validation
The skill's step 3 (`cd .factory && git branch --show-current`) returns `develop`, not `factory-artifacts`, because `.factory/` is now a regular directory containing the worktree, not the worktree itself. The skill interprets this as "worktree on wrong branch" and (per the documented recovery) would `git worktree remove .factory --force` — but that fails because `.factory/` is not a worktree; only `.factory/.factory/` is.
## Possible root cause
When `git worktree add ` is called and `` is a directory that exists with content, git appears (in some race conditions) to nest the worktree inside it as `/`. I have not been able to repro this in isolation outside the dispatcher race window, so the dispatcher's continued writes to `.factory/logs/` may be the trigger.
## Proposed fix
### Validate the worktree mount path after `git worktree add`
Factory-health step 2 should, after `git worktree add`:
```bash
actual=\$(git -C .factory rev-parse --show-toplevel)
expected="\$(pwd)/.factory"
if [ "\$actual" != "\$expected" ]; then
echo "FATAL: worktree mounted at \$actual, expected \$expected — possible dispatcher race"
# Remove and retry with dispatcher paused, or abort with clear message
git worktree remove "\$actual" --force
exit 1
fi
```
### Pause the dispatcher during factory-health repair
`/vsdd-factory:factory-health` should signal the dispatcher to stop writing to `.factory/logs/` for the duration of the repair. This could be:
- An env var / sentinel file the dispatcher checks (e.g., `.factory-health-in-progress`)
- A `factory-obs pause` / `factory-obs resume` pair invoked around the worktree-add
### Stronger: refuse to `git worktree add` to a non-empty directory
The current `git worktree add` behavior (nesting silently when the target is non-empty) is the surprise. Skill should `rm -rf .factory/` (after preserving any data) or `test ! -e .factory/` before invoking the worktree add.
## Acceptance criteria
- [ ] After `git worktree add .factory factory-artifacts`, the skill validates `git -C .factory rev-parse --show-toplevel` equals `/.factory` and aborts with a clear error if not.
- [ ] The dispatcher does not write to `.factory/logs/` during the factory-health repair window.
- [ ] The nested-mount case (worktree at `.factory/.factory/`) is detected and cleaned up automatically on re-run.
## Found during
`/vsdd-factory:factory-health` on `switchboard-blue` (2026-06-23, `vsdd-factory@1.0.0-rc.21`), immediately after `/vsdd-factory:onboard-observability`. The dispatcher was writing `dispatcher-internal-2026-06-23.jsonl` into `.factory/logs/` continuously, racing with the worktree-add step.
## Related
- #130 (closed) — recursive `.factory/.factory/logs/` shadow. Different code path but adjacent symptom.
Contributor guide
Assessment
This issue has not been assessed yet.