Skip to content

[refactor] Semantic function clustering analysis: persistent duplicates, stub files, and new go.mod parsing overlap #20242

@github-actions

Description

@github-actions

Automated semantic analysis of 546 non-test Go files across 18 packages in pkg/. Three issues from the prior analysis remain unresolved; one new duplicate was detected.

Summary

  • Total Go files analyzed: 546 (across pkg/cli, pkg/workflow, and 16 utility packages)
  • Outliers found: 2 confirmed (empty stub files)
  • Duplicates detected: 3 confirmed (1 new, 2 carry-over)
  • Mixed concerns: 1 confirmed (carry-over)

Issue 1 — Duplicate truncateSnippet() vs stringutil.Truncate() (carry-over)

A private truncateSnippet() in pkg/workflow/markdown_security_scanner.go reimplements truncation logic already available in pkg/stringutil.

truncateSnippet stringutil.Truncate
File pkg/workflow/markdown_security_scanner.go:798 pkg/stringutil/stringutil.go:17
Behavior strings.TrimSpace(s) then truncate with "..." Truncate with "...", handles maxLen ≤ 3 edge case
Used 10+ call sites within the same file Throughout the codebase
// markdown_security_scanner.go:798 — private duplicate
func truncateSnippet(s string, maxLen int) string {
    s = strings.TrimSpace(s)
    if len(s) <= maxLen { return s }
    return s[:maxLen] + "..."
}

// stringutil/stringutil.go:17 — canonical implementation
func Truncate(s string, maxLen int) string {
    if len(s) <= maxLen { return s }
    if maxLen <= 3 { return s[:maxLen] }
    return s[:maxLen-3] + "..."
}

Recommendation: Remove truncateSnippet; replace call sites with strings.TrimSpace inline + stringutil.Truncate(), or add a TrimAndTruncate helper to stringutil.


Issue 2 — Overlapping normalizeWorkflowID() vs stringutil.NormalizeWorkflowName() (carry-over)

pkg/cli/commands.go defines a private normalizeWorkflowID() that overlaps with the public stringutil.NormalizeWorkflowName().

normalizeWorkflowID stringutil.NormalizeWorkflowName
File pkg/cli/commands.go:153 pkg/stringutil/identifiers.go:29
Handles paths Yes (filepath.Base first) No (name only)
Strips .lock.yml No Yes

Recommendation: Extend stringutil.NormalizeWorkflowName to accept paths (calling filepath.Base internally), or document the distinction clearly to prevent future confusion.


Issue 3 — Near-duplicate parseGoMod() vs parseGoModWithIndirect() (new)

Two files implement nearly identical go.mod parsing logic, differing only in whether indirect dependencies are included in results.

parseGoMod parseGoModWithIndirect
File pkg/cli/deps_outdated.go pkg/cli/deps_report.go
Returns []DependencyInfo (direct only) []DependencyInfoWithIndirect (all)
Code similarity ~90% identical ~90% identical
View code comparison

Both functions share:

  • Identical file reading and error handling
  • Identical require ( block tracking
  • Identical line-by-line parsing with strings.Fields

The only difference: parseGoMod skips lines containing // indirect, while parseGoModWithIndirect captures the indirect flag and wraps DependencyInfo in DependencyInfoWithIndirect.

Recommendation: Consolidate into a single parseGoModFile(path string) ([]DependencyInfoWithIndirect, error) in a shared file (e.g., deps_helpers.go). Both callers can then filter on the Indirect field as needed.


Issue 4 — Empty stub files in pkg/workflow (carry-over)

Two files exist containing only package declarations with no active code:

  • pkg/workflow/compiler_safe_outputs_shared.go — 1 line (package workflow only)
  • pkg/workflow/compiler_safe_outputs_discussions.go — 6 lines (package declaration + comment explaining prior content was removed)
// compiler_safe_outputs_discussions.go — pure comment, no code
package workflow

// This file previously contained discussion-related step config builders.
// Those functions have been removed as discussions are now handled by the
// safe output handler manager (safe_output_handler_manager.cjs).
// See pkg/workflow/compiler_safe_outputs_core.go for the new implementation.

Recommendation: Delete both files. The explanatory comment in compiler_safe_outputs_discussions.go can be moved to compiler_safe_outputs_core.go if the context is valuable.


Issue 5 — Mixed concerns in pkg/cli/commands.go (carry-over)

commands.go acts as a catch-all file combining four unrelated concerns:

Concern Functions Lines
Version management SetVersionInfo(), GetVersion() 36–44
Agent file download downloadAgentFileFromGitHub(), downloadAgentFileViaGHCLI(), patchAgentFileURLs(), isGHCLIAvailable() 47–151
Workflow file resolution normalizeWorkflowID(), resolveWorkflowFile(), resolveWorkflowFileInDir() 153–228
Workflow creation NewWorkflow(), createWorkflowTemplate() 229–end

Recommendation: Split into focused files:

  • version.go ← version management
  • agent_download.go ← agent file download
  • Move resolveWorkflowFile* to workflows.go (which already has getMarkdownWorkflowFiles())

Implementation Checklist

  • Remove truncateSnippet() from pkg/workflow/markdown_security_scanner.go; replace with strings.TrimSpace + stringutil.Truncate()
  • Resolve overlap between normalizeWorkflowID() and stringutil.NormalizeWorkflowName() — consolidate or document
  • Consolidate parseGoMod() and parseGoModWithIndirect() into a single shared function in pkg/cli/deps_helpers.go
  • Delete pkg/workflow/compiler_safe_outputs_shared.go and pkg/workflow/compiler_safe_outputs_discussions.go
  • Split pkg/cli/commands.go into version.go and agent_download.go

Analysis Metadata

  • Total Go files analyzed: 546
  • Packages covered: pkg/cli, pkg/workflow, pkg/stringutil, pkg/sliceutil, pkg/mathutil, pkg/fileutil, pkg/gitutil, pkg/timeutil, pkg/envutil, pkg/repoutil, pkg/types, pkg/constants, pkg/parser, and others
  • Detection method: Serena LSP semantic analysis + naming pattern clustering + cross-package comparison
  • Workflow run: §22865593427

References:

Generated by Semantic Function Refactoring ·

  • expires on Mar 11, 2026, 5:21 PM UTC

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions