BOHICA-LABS / BOHICA-LABS/vsdd-factory

bug(factory-health): 'git worktree add .factory factory-artifacts' silently mounts at .factory/.factory/ when racing with dispatcher

Open
#205 1 comment 0 reactions 0 assignees View on GitHub
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.