# Empty-repo hardening — implementation plan Spec: `docs/superpowers/specs/2026-07-30-empty-repo-hardening-design.md` ## Task 1 — GitService: HasHeadCommitAsync + EnsureHeadCommitAsync + friendly unborn error **Files:** `src/ClaudeDo.Data/Git/GitService.cs`, `tests/ClaudeDo.Worker.Tests/Runner/GitServiceBootstrapTests.cs` (new) 1. Tests first (skip when git unavailable, mirroring `WorktreeManagerTests`): - fresh `git init` repo (no commit): `HasHeadCommitAsync` false; after a commit true. - `EnsureHeadCommitAsync` on empty repo → true; `RevParseHeadAsync` then succeeds; `git ls-tree HEAD` empty; second call → false. - staged file in empty repo survives bootstrap: still staged, not in the commit. - corrupt HEAD (write a garbage SHA into `.git/HEAD` as a detached head): `EnsureHeadCommitAsync` throws `InvalidOperationException` mentioning the dir. - `RevParseHeadAsync` on empty repo: message says "has no commits yet" (no raw "ambiguous argument"). 2. Implement: `HasHeadCommitAsync` (`rev-parse --verify --quiet HEAD`), `EnsureHeadCommitAsync` (symbolic-ref guard → hash-object/commit-tree/update-ref plumbing behind a static `SemaphoreSlim` with post-acquire re-check), unborn-HEAD message in `RevParseHeadAsync`. 3. Build Data + run new tests. Commit `fix(git): bootstrap empty repos with an initial commit`. ## Task 2 — CommitAsync identity fallback **Files:** same two files. 1. Test: repo with local `user.name`/`user.email` set to empty strings → `CommitAsync` succeeds (retry path), commit exists. 2. Implement: on identity-signature failure, retry once with `-c user.name=ClaudeDo -c user.email=claudedo@local`. 3. Commit `fix(git): fall back to a ClaudeDo identity when git has none`. ## Task 3 — call sites **Files:** `src/ClaudeDo.Worker/Runner/WorktreeManager.cs`, `src/ClaudeDo.Worker/Planning/PlanningSessionManager.cs`, `tests/ClaudeDo.Worker.Tests/Runner/WorktreeManagerTests.cs` 1. Test: `CreateAsync` on a commit-less repo succeeds; worktree on disk; DB row Active. 2. Wire `EnsureHeadCommitAsync` into `WorktreeManager.CreateAsync` (log info on bootstrap) and `PlanningSessionManager.StartAsync`. 3. Full Worker.Tests run. Commit `fix(worktree): auto-bootstrap commit-less repos before creating worktrees`. ## Task 4 — docs - `src/ClaudeDo.Data/CLAUDE.md`: GitService bullet gains bootstrap/identity-fallback. - `docs/open.md`: verification item — open a ConPTY session on a fresh `git init` repo on a second PC. - Commit `docs: empty-repo hardening notes`. ## Verification - `dotnet test tests/ClaudeDo.Worker.Tests -c Release` green. - Manual: task 61609f79… in "Test List" (`C:\Dev\TestRepo`, currently commit-less) — opening the ConPTY session must now succeed and leave `C:\Dev\TestRepo` with exactly one empty commit by ClaudeDo.