Skip to content

[refactor] Semantic function clustering analysis: minor refactoring opportunities in pkg/workflow #28586

@github-actions

Description

@github-actions

Analysis of 727 non-test Go source files across 22 packages in pkg/. The codebase is well-organized overall, with deliberate use of generics, helper-file conventions, and consolidation patterns. Two concrete refactoring opportunities were identified.

Overall Assessment

The repository demonstrates strong architectural patterns:

  • Validation consolidation via validation_helpers.go (~76 helpers shared across 40+ validator files)
  • Generic infrastructure in update_entity_helpers.go and close_entity_helpers.go
  • Clear separation of concerns in the compile_* and mcp_renderer_* file groups
  • Appropriate package boundaries between stringutil, repoutil, gitutil, timeutil, etc.

No misplaced functions or cross-package duplicates were found. The two findings below are both confined to pkg/workflow.


Finding 1: setStringFromMap duplicates extractStringFromMap in the same package

Files:

  • pkg/workflow/safe_outputs_messages_config.go:17 — defines setStringFromMap
  • pkg/workflow/config_helpers.go:95 — defines extractStringFromMap

Problem: Both functions extract a string value from a map[string]any by key. They exist in the same workflow package and do the same thing, differing only in their call signature.

// safe_outputs_messages_config.go:16-22
// setStringFromMap reads m[key] and assigns its string value to *dest if found.
func setStringFromMap(m map[string]any, key string, dest *string) {
    if val, exists := m[key]; exists {
        if str, ok := val.(string); ok {
            *dest = str
        }
    }
}

// config_helpers.go:95-105
func extractStringFromMap(m map[string]any, key string, log *logger.Logger) string {
    if value, exists := m[key]; exists {
        if valueStr, ok := value.(string); ok {
            return valueStr
        }
    }
    return ""
}

setStringFromMap is called 14 times in parseMessagesConfig and exists only in that file. It could be removed and callers replaced with direct assignments using extractStringFromMap(m, key, nil).

Recommendation: Remove setStringFromMap and replace its 14 call sites with extractStringFromMap(messagesMap, key, nil) assignments:

// Before
setStringFromMap(messagesMap, "footer", &config.Footer)

// After
config.Footer = extractStringFromMap(messagesMap, "footer", nil)

Estimated effort: ~30 minutes
Impact: Removes one redundant helper, reduces in-package duplication


Finding 2: permissions_factory.go — 20 hardcoded permission-set constructors

File: pkg/workflow/permissions_factory.go

Pattern: 20 named constructor functions, 14 of which each call NewPermissionsFromMap with a hardcoded combination of PermissionScope → PermissionLevel pairs:

func NewPermissionsContentsRead() *Permissions {
    return NewPermissionsFromMap(map[PermissionScope]PermissionLevel{
        PermissionContents: PermissionRead,
    })
}

func NewPermissionsContentsReadIssuesWrite() *Permissions {
    return NewPermissionsFromMap(map[PermissionScope]PermissionLevel{
        PermissionContents: PermissionRead,
        PermissionIssues:   PermissionWrite,
    })
}
// ... 12 more like this

These functions serve as named semantic presets (documenting which workflows require which permissions), but the naming convention encodes the full permission set — e.g., NewPermissionsContentsReadIssuesWriteDiscussionsWrite — which becomes increasingly verbose for combinations of 3+ scopes.

Recommendation (optional): Consider replacing the 14 map-based constructors with named var declarations:

var (
    PermissionsContentsRead = NewPermissionsFromMap(map[PermissionScope]PermissionLevel{
        PermissionContents: PermissionRead,
    })
    PermissionsContentsReadIssuesWrite = NewPermissionsFromMap(map[PermissionScope]PermissionLevel{
        PermissionContents: PermissionRead,
        PermissionIssues:   PermissionWrite,
    })
    // ...
)

This eliminates function call overhead and groups the presets as readable constants. However, this is a style choice — the current approach is not incorrect, just verbose.

Estimated effort: 1–2 hours
Impact: Reduced line count, clearer intent (presets vs constructors)


Analysis Metadata

Metric Value
Go source files analyzed 727
Test files skipped ~300+
Total functions cataloged ~3,478
Packages examined 22
Clusters identified 8 major groups
Outliers found 0
Confirmed duplicates 1 (setStringFromMap)
Patterns worth reviewing 1 (permissions_factory.go)
Detection method Serena LSP + naming pattern analysis
Analysis date 2026-04-26
Workflow run §24955479019
Well-organized patterns (no action needed)
  • validation_helpers.go consolidates 76+ shared validation patterns across 40+ validators — effective
  • update_entity_helpers.go uses Go generics to unify issue/PR/discussion/release update parsers — well-designed
  • close_entity_helpers.go uses a registry pattern to eliminate 3+ separate config files
  • compile_*.go split across 6 files with clear responsibility boundaries (config, orchestrator, pipeline, setup, infrastructure, stats) — appropriate
  • mcp_renderer_*.go correctly split by target format and tool type
  • stringutil vs workflow/strings.go: intentional separation of general-purpose normalization (stringutil) from workflow-specific sanitization (strings.go), explicitly documented
  • gitutil.ExtractBaseRepo vs repoutil.SplitRepoSlug: different semantics (path prefix extraction vs strict slug validation) — not duplicates
  • All utility packages (sliceutil, typeutil, fileutil, semverutil, timeutil, envutil) have appropriately scoped, non-overlapping functions

Generated by Semantic Function Refactoring · ● 627K ·

  • expires on Apr 28, 2026, 11:36 AM UTC

Metadata

Metadata

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