Skip to content

[refactor] Semantic Function Clustering Analysis: Duplicates and Refactoring Opportunities #17244

Description

@github-actions

This report summarizes findings from a semantic function clustering analysis of all non-test Go files in the pkg/ directory. Analysis covered 553 files and approximately 2,742 functions across 18 sub-packages.


Executive Summary

The analysis identified 5 concrete refactoring opportunities with medium-to-high impact, including near-duplicate implementations of utility functions, functions that should use existing shared utilities, a file naming inconsistency, and a highly repetitive engine-specific API pattern in domains.go that could be simplified.

Category Count Impact
Near-duplicate functions (vs. existing utilities) 2 High
Thin wrapper functions (trivial indirection) 1 Medium
Semver logic scattered across packages 2 files Medium
Repetitive engine-specific function pattern 15 functions Medium
File naming inconsistency 1 file Low

Issue 1: truncateString Duplicates stringutil.Truncate

File: pkg/cli/gateway_logs.go
Existing utility: pkg/stringutil/stringutil.go

The function truncateString (private, in gateway_logs.go) is a near-identical reimplementation of the exported stringutil.Truncate:

View Code Comparison
// pkg/cli/gateway_logs.go:378
func truncateString(s string, maxLen int) string {
    if len(s) <= maxLen {
        return s
    }
    if maxLen <= 3 {
        return s[:maxLen]
    }
    return s[:maxLen-3] + "..."
}

// pkg/stringutil/stringutil.go:16
func Truncate(s string, maxLen int) string {
    if len(s) <= maxLen {
        return s
    }
    if maxLen <= 3 {
        return s[:maxLen]
    }
    ...
}

Recommendation: Replace truncateString in gateway_logs.go with stringutil.Truncate. This eliminates the duplicate and ensures consistent truncation behavior across the codebase.


Issue 2: uniqueStrings Duplicates sliceutil.Deduplicate

File: pkg/cli/codemod_mcp_network.go:423
Existing utility: pkg/sliceutil/sliceutil.go

uniqueStrings is a private function that implements the exact same logic as sliceutil.Deduplicate[string] which already exists in the shared utility package:

View Code Comparison
// pkg/cli/codemod_mcp_network.go:423
func uniqueStrings(input []string) []string {
    seen := make(map[string]bool)
    var result []string
    for _, item := range input {
        if !seen[item] {
            seen[item] = true
            result = append(result, item)
        }
    }
    return result
}

// pkg/sliceutil/sliceutil.go
func Deduplicate[T comparable](slice []T) []T {
    seen := make(map[T]bool, len(slice))
    result := make([]T, 0, len(slice))
    for _, item := range slice {
        if !seen[item] {
            seen[item] = true
            result = append(result, item)
        }
    }
    return result
}

Recommendation: Replace uniqueStrings(input) calls in codemod_mcp_network.go with sliceutil.Deduplicate(input). The generic version is more efficient (pre-allocates the result slice) and avoids duplication.


Issue 3: SortStrings is a Trivial Wrapper of sort.Strings

File: pkg/workflow/strings.go:106

SortStrings is a single-line function that only wraps the standard library sort.Strings:

// pkg/workflow/strings.go:106
func SortStrings(s []string) {
    sort.Strings(s)
}

This adds a layer of indirection with no benefit — callers could use sort.Strings directly.

Recommendation: Audit callers of SortStrings within pkg/workflow/ and replace them with direct sort.Strings calls, then remove the wrapper. This reduces the public API surface of the workflow package.


Issue 4: Semver Logic Split Across Two Packages

Files: pkg/cli/semver.go and pkg/workflow/semver.go

Both packages define private semver-related functions using the same golang.org/x/mod/semver library, but with non-overlapping functionality that is closely related:

View Function Inventory

pkg/cli/semver.go:

  • isSemanticVersionTag(ref string) bool — validates if a ref is a semver tag
  • parseVersion(v string) *semanticVersion — parses a semver string into struct
  • parseInt(s string) int — parses an integer from string (note: reimplements strconv.Atoi)
  • (v *semanticVersion) isPreciseVersion() bool — checks if version has minor+patch
  • (v *semanticVersion) isNewer(other *semanticVersion) bool — compares two versions

pkg/workflow/semver.go:

  • compareVersions(v1, v2 string) int — compares two version strings
  • extractMajorVersion(version string) int — extracts the major version number
  • isSemverCompatible(pinVersion, requestedVersion string) bool — checks major version compatibility

Both files duplicate the same pattern of normalizing version strings with a v prefix before delegating to golang.org/x/mod/semver.

Additionally, parseInt in pkg/cli/semver.go reimplements the functionality of strconv.Atoi without using it.

Recommendation:

  1. Consider consolidating into a shared pkg/semverutil package to avoid future duplication as new engines or update-checking logic is added.
  2. In pkg/cli/semver.go, replace the parseInt function with strconv.Atoi from the standard library.

Issue 5: Highly Repetitive Engine-Specific Domain Functions

File: pkg/workflow/domains.go (691 lines, 15 GetXxxAllowedDomains functions)

domains.go contains a highly repetitive family of 15 functions that follow an identical pattern — each calls the same set of internal mergeDomainsWithNetwork* helpers, differing only in which engine's default domain constant is used:

View Repetitive Pattern
// Copilot variants
func GetCopilotAllowedDomains(network *NetworkPermissions) string {
    return GetCopilotAllowedDomainsWithSafeInputs(network, false)
}
func GetCopilotAllowedDomainsWithSafeInputs(network *NetworkPermissions, hasSafeInputs bool) string {
    return mergeDomainsWithNetwork(CopilotDefaultDomains, network)
}
func GetCopilotAllowedDomainsWithTools(network *NetworkPermissions, tools map[string]any) string {
    return mergeDomainsWithNetworkAndTools(CopilotDefaultDomains, network, tools)
}
func GetCopilotAllowedDomainsWithToolsAndRuntimes(network *NetworkPermissions, tools map[string]any, runtimes map[string]any) string {
    return mergeDomainsWithNetworkToolsAndRuntimes(CopilotDefaultDomains, network, tools, runtimes)
}

// Claude variants (identical pattern, different constant)
func GetClaudeAllowedDomains(network *NetworkPermissions) string {
    return GetClaudeAllowedDomainsWithSafeInputs(network, false)
}
func GetClaudeAllowedDomainsWithSafeInputs(network *NetworkPermissions, hasSafeInputs bool) string {
    return mergeDomainsWithNetwork(ClaudeDefaultDomains, network)
}
// ... 9 more functions following the same pattern for Codex and Gemini

Each new engine requires adding 3-4 nearly identical functions. Currently there are variants for Copilot, Claude, Codex, and Gemini. The hasSafeInputs parameter in *WithSafeInputs functions is notably unused (both Copilot and Claude variants ignore it in the function body).

Recommendation: Introduce a parameterized or engine-keyed API:

// Option A: single function with engine parameter
func GetAllowedDomainsForEngine(engine CodingAgentEngine, network *NetworkPermissions, tools map[string]any, runtimes map[string]any) string

// Option B: map-based lookup of default domains by engine
var engineDefaultDomains = map[CodingAgentEngine][]string{
    EngineClaudeCode: ClaudeDefaultDomains,
    EngineCopilot:   CopilotDefaultDomains,
    // ...
}

This would reduce 15 functions to 1-3, and make adding new engines trivial.


Issue 6: copilot-agents.go File Naming Inconsistency

File: pkg/cli/copilot-agents.go

This is the only file in the entire pkg/ directory that uses a hyphen in its filename. All other files follow the standard Go convention of using underscores (e.g., copilot_agent.go, copilot_setup.go).

Recommendation: Rename pkg/cli/copilot-agents.go to pkg/cli/copilot_agents.go to match Go naming conventions and the rest of the codebase.


Detailed Function Cluster Analysis

View Full Clustering Summary by Package

pkg/workflow (276 files, primary analysis area)

Cluster File Pattern Description
Validation *_validation.go (20+ files) Well-organized, validation per feature
Creation create_*.go (6 files) Each creation action in its own file
Safe Outputs safe_outputs_*.go (10 files) Well-organized feature cluster
Engine helpers *_engine*.go (7 files) Engine-specific logic, well-separated
MCP config mcp_*.go (15 files) MCP configuration, well-organized
Runtime runtime_*.go (6 files) Runtime detection and setup
Expression expression_*.go (6 files) Expression building/parsing/validation
Bundler bundler*.go (7 files) JavaScript bundling
WASM stubs *_wasm.go (8 files) Platform stubs with build tags ✓

Outliers identified: SortStrings in strings.go (trivial wrapper)

pkg/cli (187 files)

Cluster File Pattern Description
Compile compile_*.go (10 files) Workflow compilation
Logs logs_*.go (10 files) Log fetching and display
MCP mcp_*.go (15 files) MCP server management
Codemod codemod_*.go (15 files) Automated code migrations
Add add_*.go (6 files) Workflow addition
Update update_*.go (7 files) Workflow update
Run run_*.go (5 files) Workflow execution

Outliers identified: truncateString (should use stringutil), uniqueStrings (should use sliceutil)

pkg/parser (37 files)

content_extractor.go uses a well-designed pattern with a generic extractFrontmatterField helper and thin named wrappers per field. This is a good design, not a concern.


Implementation Checklist

  • High Priority: Replace truncateString in pkg/cli/gateway_logs.go with stringutil.Truncate
  • High Priority: Replace uniqueStrings in pkg/cli/codemod_mcp_network.go with sliceutil.Deduplicate
  • Medium Priority: Replace parseInt in pkg/cli/semver.go with strconv.Atoi
  • Medium Priority: Remove SortStrings from pkg/workflow/strings.go; use sort.Strings directly at call sites
  • Medium Priority: Evaluate consolidating pkg/cli/semver.go and pkg/workflow/semver.go into a shared utility
  • Medium Priority: Refactor engine-specific domain functions in pkg/workflow/domains.go to use a single parameterized function; remove unused hasSafeInputs parameter from *WithSafeInputs variants
  • Low Priority: Rename pkg/cli/copilot-agents.gopkg/cli/copilot_agents.go

Analysis Metadata

Total Go Files Analyzed 553
Total Functions Cataloged ~2,742
Packages Analyzed 18
Outliers Found 6 issues across 7 files
Detection Method Naming pattern analysis + code comparison + Serena semantic analysis
Analysis Date 2026-02-20
Workflow Run §22233602384

Generated by Semantic Function Refactoring

  • expires on Feb 22, 2026, 5:22 PM UTC

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions