# T0.3: Git & Locking ## Scope Implement `action/git.go` + `internal/lock/flock.go` for repo cloning, worktree management, and squash-merge. ## Implementation ### File: `internal/lock/flock.go` ```go package lock // Acquire advisory file lock (blocking) func Acquire(path string) error // Release advisory file lock func Release(path string) error ``` ### File: `action/git.go` ```go type CloneRepoInput struct { RemoteURL string TargetRepoPath string } func CloneRepoActivity(ctx context.Context, in CloneRepoInput) error // If $TargetRepoPath/.git exists: git -C $TargetRepoPath fetch origin // Else: git clone $RemoteURL $TargetRepoPath type GitWorktreeAddInput struct { RepoPath string TaskID string } func GitWorktreeAddActivity(ctx context.Context, in GitWorktreeAddInput) (string, error) // Guarded by orchestrator.lock // git worktree add -b task/ ../worktrees/ origin/main // Return worktree path type GitCommitInput struct { WorktreePath string Message string } func GitCommitActivity(ctx context.Context, in GitCommitInput) error // No lock needed; safe within isolated worktree // git -C $WorktreePath add -A // git -C $WorktreePath commit -m "$Message" type GitPushInput struct { RepoPath string } func GitPushActivity(ctx context.Context, in GitPushInput) error // Guarded by orchestrator.lock // git -C $RepoPath push origin main type GitSquashMergeInput struct { RepoPath string Branches []string // ["task/T0.1", "task/T0.2", ...] Message string } func GitSquashMergeActivity(ctx context.Context, in GitSquashMergeInput) error // Guarded by orchestrator.lock // fetch origin main // checkout main && pull --ff-only origin main // for b in branches: merge --squash $b // commit -m $Message // push origin main // for b in branches: worktree remove + branch -D ``` ## Verification ```bash cd /Users/rockliang/workplace/Poimen/workflows go test -v ./tests -run TestGit # Test script: tests/git_test.go ``` Test cases: - Clone into empty path → creates .git - Clone into existing path → fetches instead of re-cloning - Worktree add → returns valid path - Commit in worktree → file changes staged - Squash-merge → one commit on main, branches cleaned up ## Done Criteria - `go test ./tests -run TestGit` passes - Tested against local scratch git repo (not real remote) - No lock deadlocks on concurrent calls - Squash-merge produces exactly one commit