fix(routing): default to Maintainer when no git remote exists (#1185)
When no git remote is configured, DetectUserRole() now defaults to Maintainer instead of Contributor. This fixes issue routing for: 1. New personal projects (no remote configured yet) 2. Intentionally local-only repositories Previously, issues would silently route to ~/.beads-planning instead of the local .beads/ directory. Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,7 @@ const (
|
||||
// Detection strategy:
|
||||
// 1. Check if user has push access to origin (git remote -v shows write URL)
|
||||
// 2. Check git config for beads.role setting (explicit override)
|
||||
// 3. Fall back to contributor if uncertain
|
||||
// 3. Fall back to maintainer for local projects (no remote configured)
|
||||
func DetectUserRole(repoPath string) (UserRole, error) {
|
||||
// First check for explicit role in git config
|
||||
output, err := gitCommandRunner(repoPath, "config", "--get", "beads.role")
|
||||
@@ -49,8 +49,8 @@ func DetectUserRole(repoPath string) (UserRole, error) {
|
||||
// Fallback to standard fetch URL if push URL fails (some git versions/configs)
|
||||
output, err = gitCommandRunner(repoPath, "remote", "get-url", "origin")
|
||||
if err != nil {
|
||||
// No remote or error - default to contributor
|
||||
return Contributor, nil
|
||||
// No remote means local project - default to maintainer
|
||||
return Maintainer, nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,13 +83,13 @@ func TestDetermineTargetRepo(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDetectUserRole_Fallback(t *testing.T) {
|
||||
// Test fallback behavior when git is not available
|
||||
// Test fallback behavior when git is not available - local projects default to maintainer
|
||||
role, err := DetectUserRole("/nonexistent/path/that/does/not/exist")
|
||||
if err != nil {
|
||||
t.Fatalf("DetectUserRole() error = %v, want nil", err)
|
||||
}
|
||||
if role != Contributor {
|
||||
t.Errorf("DetectUserRole() = %v, want %v (fallback)", role, Contributor)
|
||||
if role != Maintainer {
|
||||
t.Errorf("DetectUserRole() = %v, want %v (local project fallback)", role, Maintainer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ func TestDetectUserRole_HTTPSCredentialsMaintainer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectUserRole_DefaultContributor(t *testing.T) {
|
||||
func TestDetectUserRole_HTTPSNoCredentialsContributor(t *testing.T) {
|
||||
orig := gitCommandRunner
|
||||
stub := &gitStub{t: t, responses: []gitResponse{
|
||||
{expect: gitCall{"", []string{"config", "--get", "beads.role"}}, err: errors.New("missing")},
|
||||
@@ -289,6 +289,29 @@ func TestDetectUserRole_DefaultContributor(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDetectUserRole_NoRemoteMaintainer(t *testing.T) {
|
||||
// When no git remote is configured, default to maintainer (local project)
|
||||
orig := gitCommandRunner
|
||||
stub := &gitStub{t: t, responses: []gitResponse{
|
||||
{expect: gitCall{"/local", []string{"config", "--get", "beads.role"}}, err: errors.New("missing")},
|
||||
{expect: gitCall{"/local", []string{"remote", "get-url", "--push", "origin"}}, err: errors.New("no remote")},
|
||||
{expect: gitCall{"/local", []string{"remote", "get-url", "origin"}}, err: errors.New("no remote")},
|
||||
}}
|
||||
gitCommandRunner = stub.run
|
||||
t.Cleanup(func() {
|
||||
gitCommandRunner = orig
|
||||
stub.verify()
|
||||
})
|
||||
|
||||
role, err := DetectUserRole("/local")
|
||||
if err != nil {
|
||||
t.Fatalf("DetectUserRole error = %v", err)
|
||||
}
|
||||
if role != Maintainer {
|
||||
t.Fatalf("expected %s for local project with no remote, got %s", Maintainer, role)
|
||||
}
|
||||
}
|
||||
|
||||
// TestFindTownRoutes_SymlinkedBeadsDir verifies that findTownRoutes correctly
|
||||
// handles symlinked .beads directories by using findTownRootFromCWD() instead of
|
||||
// walking up from the beadsDir path.
|
||||
|
||||
Reference in New Issue
Block a user