Skip to content

[sergo] pkg/cli: inconsistent constructor error-return pattern — NewFileTracker vs NewMCPRegistryClient #30933

@github-actions

Description

@github-actions

Problem

The cli package has inconsistent constructor conventions for non-command types. NewFileTracker returns (*FileTracker, error) because it validates external state at construction time (git root must exist). However, other non-cobra constructors do not follow this pattern:

Constructor Return type Does external I/O?
NewFileTracker() (*FileTracker, error) Yes — runs gitutil.FindGitRoot()
NewMCPRegistryClient(url) *MCPRegistryClient Creates http.Client (no error possible)
NewDependencyGraph(dir) *DependencyGraph Stores path, no validation
NewToolGraph() *ToolGraph Pure memory allocation
NewCopilotCodingAgentDetector(...) *CopilotCodingAgentDetector Stores path, no validation

The root cause: NewFileTracker eagerly validates its precondition (requires a git repo) while all others defer validation to method calls. This creates a surprising inconsistency — callers must handle an error from NewFileTracker but never from similar constructors.

This extends a pattern previously identified in agentdrain (Run 2, 2026-05-07), where all 4 constructors consistently return errors — unlike cli which is mixed.

Location

  • pkg/cli/file_tracker.go:27NewFileTracker
  • pkg/cli/mcp_registry.go:39NewMCPRegistryClient
  • pkg/cli/dependency_graph.go:32NewDependencyGraph
  • pkg/cli/tool_graph.go:30NewToolGraph

Impact

  • Severity: Medium
  • Affected code: cli package constructor API
  • Risk: Callers are uncertain when to expect errors from constructors. A future constructor that validates may be written without an error return, masking failures until method calls.

Recommendation

Pick one of two consistent options and apply it across the cli non-command constructors:

Option A (Lazy validation — preferred for CLI):
Remove eager validation from NewFileTracker. Move git root lookup to the first method that actually needs it (e.g., TrackCreated or Commit). All non-command constructors then return only *T.

// Before
func NewFileTracker() (*FileTracker, error) {
    gitRoot, err := gitutil.FindGitRoot()
    if err != nil { return nil, err }
    return &FileTracker{OriginalContent: make(map[string][]byte), gitRoot: gitRoot}, nil
}

// After
func NewFileTracker() *FileTracker {
    return &FileTracker{OriginalContent: make(map[string][]byte)}
}
// gitRoot resolved lazily in the method that needs it

Option B (Eager validation):
Add validation to other constructors that have meaningful preconditions (e.g., validate workflowsDir exists in NewDependencyGraph).

Option A is recommended since CLI commands often construct objects before confirming they're in a git repo, and lazy errors produce clearer messages at use-time.

Validation

  • Update all callers of NewFileTracker to remove error handling (or handle at method call)
  • Run existing CLI tests
  • Verify file tracker behavior unchanged in git and non-git contexts

Estimated Effort: Small


Generated by Sergo (Run 3, 2026-05-08) — Run ID: §25537174291

Generated by Sergo - Serena Go Expert · ● 506.4K ·

  • expires on May 15, 2026, 4:52 AM UTC

Metadata

Metadata

Labels

cookieIssue Monster Loves Cookies!sergo

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions