Files
ClaudeDo/docs/superpowers/plans/2026-07-30-empty-repo-hardening-plan.md
T
CubeGameLP fea8236e8a docs(git): spec + plan for empty-repo (unborn HEAD) hardening
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 5f4b7a9e26af0635cfaecfbc55bfe928dd3466d2)
2026-08-28 20:31:50 +02:00

2.8 KiB

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.