Skip to content

[refactor] Semantic Function Clustering Analysis — Updated Findings (Micro-files, Outliers, Package Fragmentation) #1712

@github-actions

Description

@github-actions

Overview

Analysis of 75 non-test Go files across 22 packages catalogued ~529 functions/methods. The codebase continues to be generally well-organized. Five findings from the previous analysis remain unresolved, and one new finding has been introduced since that run: a new internal/dockerutil/ single-function package that fragments Docker-related utilities.

Full Report

Analysis Metadata

  • Total Go Files Analyzed: 75 (72 in internal/, 3 at root: main.go, tools.go, version.go)
  • Total Functions Catalogued: ~529
  • Packages Analyzed: 22 internal packages
  • Outliers Found: 3 (see below)
  • Micro-files Found: 5 (see below)
  • New Package Fragmentation: 1 (internal/dockerutil/)
  • Detection Method: Naming pattern analysis + function signature review + cross-package usage analysis
  • Analysis Date: 2026-03-09
  • Previous Issue: [refactor] Semantic Function Clustering Analysis — Micro-files, Outlier Functions, and Ad-hoc Utilities #1678 (closed — all findings remain unresolved)

Identified Issues

1. 🔴 internal/tty/ — Two Single-Function Micro-files

Files: internal/tty/tty.go (15 lines) and internal/tty/container.go (35 lines)

Both live in package tty and both detect environment/terminal characteristics. Neither has an import-cycle reason for its own file — the comment in container.go notes the constraint is that tty cannot import logger, which is a package-level constraint, not a file-level constraint.

File Function Lines
tty.go IsStderrTerminal() bool 15
container.go IsRunningInContainer() bool 35

Recommendation: Merge container.go into tty.go. Total file stays under 55 lines.

Estimated Effort: 10 minutes
Impact: Eliminates one file; both terminal-detection functions are discoverable in one place.


2. 🔴 NEW: internal/dockerutil/env.go — Single-Function Micro-Package

This is a new finding not present in the previous analysis.

File: internal/dockerutil/env.go (41 lines)
Package: dockerutil
Sole Function: ExpandEnvArgs(args []string) []string
Only Caller: internal/mcp/connection.go:128

// internal/dockerutil/env.go — sole function in package
func ExpandEnvArgs(args []string) []string {
    // Converts "-e VAR_NAME" to "-e VAR_NAME=value" by reading process environment
}
// internal/mcp/connection.go:128 — only call site
expandedArgs := dockerutil.ExpandEnvArgs(args)

Problem: Creating an entire package for a single utility function used in only one place adds unnecessary fragmentation and import overhead. The repository already has internal/config/docker_helpers.go (115 lines) with Docker-related utilities (checkDockerAccessible, validateContainerID, runDockerInspect, checkPortMapping, checkStdinInteractive, checkLogDirMounted).

Options:

  1. Move to internal/config/docker_helpers.go — consolidates all Docker utilities in one place. Update caller to config.ExpandEnvArgs(args).
  2. Move to internal/mcp/connection.go — since it has exactly one caller, make it a private local helper (expandEnvArgs). Most minimal change.

Recommendation: Option 2 (inline as private helper in mcp/connection.go) — it has one caller, is 20 lines of logic, and has no reason to be independently importable. If Docker env expansion logic grows, revisit.

Estimated Effort: 20 minutes
Impact: Removes one package, clarifies ownership.


3. 🟡 internal/auth/header.go:TruncateSessionID — Outlier Utility in Auth Package

File: internal/auth/header.go:172
Function:

func TruncateSessionID(sessionID string) string {
    if sessionID == "" {
        return "(none)"
    }
    if len(sessionID) <= 8 {
        return sessionID
    }
    return sessionID[:8] + "..."
}

Callers: internal/server/sdk_logging.go (5 call sites — all logging calls)

Problem: This is a string utility (truncate for safe logging), not an authentication concern. The repository already has internal/strutil/truncate.go with Truncate() and TruncateWithSuffix(). The function's purpose — safe session ID display in logs — is orthogonal to header parsing.

Comparison:

// strutil.TruncateWithSuffix (existing) — only differences are 
// the "(none)" empty-string handling and the hardcoded 8-char limit
func TruncateWithSuffix(s string, maxLen int, suffix string) string { ... }

Recommendation: Move TruncateSessionID to internal/strutil/truncate.go. Update sdk_logging.go to import strutil instead of auth for this function. Keep the function name and behaviour identical.

Estimated Effort: 20 minutes
Impact: Single source of truth for truncation; auth package stays focused on authentication logic.


4. 🟡 internal/server/transport.go — Misleading Filename

File: internal/server/transport.go (54 lines)
Sole Public Function: CreateHTTPServerForMCP(addr string, unifiedServer *UnifiedServer, apiKey string) *http.Server

Problem: The name "transport" implies a low-level transport protocol concern. Compare the asymmetry:

File Function Mode
transport.go CreateHTTPServerForMCP Unified
routed.go CreateHTTPServerForRoutedMode Routed

Both are HTTP server factory functions. One lives in a mode-named file, the other in a generic "transport" file. The actual MCP transport layer is in internal/mcp/http_transport.go, making the name doubly confusing.

Recommendation: Rename transport.gounified_server.go (or http_server_unified.go) to mirror routed.go. The logger variable logTransport would need renaming to avoid conflict with logUnified in unified.go — use logHTTPServer instead.

Estimated Effort: 15 minutes
Impact: Consistent naming across server factory files; easier discovery.


5. 🟡 internal/cmd/root.go — Config Output Functions Are Outliers

File: internal/cmd/root.go (648 lines)
Misplaced Functions (lines ~481–587):

  • writeGatewayConfigToStdout(cfg, listenAddr, mode string) error (line 481, 3 lines)
  • writeGatewayConfig(cfg, listenAddr, mode string, w io.Writer) error (line 485, ~90 lines)
  • loadEnvFile(path string) error (line 587, ~35 lines)

Problem: root.go's responsibility is CLI wiring (run, preRun, postRun, Execute). writeGatewayConfig is a ~90-line config serialization routine that builds and writes JSON per spec §5.4. This is a serialization concern, not a CLI lifecycle concern.

Recommendation: Extract to internal/cmd/config_output.go. This mirrors the existing pattern of split responsibility files in internal/cmd/ (flags_core.go, flags_logging.go, flags_difc.go, flags_launch.go).

Estimated Effort: 30 minutes
Impact: Reduces root.go from 648 → ~525 lines; config output logic is more discoverable.


6. 🟢 internal/cmd/flags_launch.go — Single-Flag Micro-file

File: internal/cmd/flags_launch.go (21 lines)
Content: One constant (defaultSequentialLaunch), one variable (sequentialLaunch), and an init() block.

Observation: The pattern of flags_*.go files is intentional in this codebase for feature-specific flag grouping. However, flags_launch.go has only a single flag compared to flags_core.go (47 lines), flags_logging.go (55 lines), and flags_difc.go (236 lines). It adds a file with negligible organizational benefit.

Recommendation: Merge into internal/cmd/flags.go (99 lines). Resulting file would be ~120 lines — still reasonable. Alternatively, keep if launch-specific flags are expected to grow.

Estimated Effort: 10 minutes
Impact: Minor — reduces file count by one.


7. 🟢 internal/config/config_payload.go — Thin Constants File

File: internal/config/config_payload.go (30 lines)
Content: 3 exported constants (DefaultPayloadDir, DefaultLogDir, DefaultPayloadSizeThreshold) + 1 init() function (10 lines) registering a DefaultsSetter.

Recommendation: Merge constants and init() into internal/config/config_core.go (307 lines → ~337 lines, still manageable). The constants define operational defaults, not a distinct feature concern.

Estimated Effort: 15 minutes
Impact: Reduces file count; groups operational defaults with core config definitions.


Well-Organized Patterns (No Changes Needed)

  • internal/logger/: 12 files with clear separation (file logger, slog adapter, sanitize, RPC formatters). global_helpers.go correctly centralizes init/close.
  • internal/difc/: DIFC labels/evaluator/resource split is semantically clear; no outliers.
  • internal/config/: The config_*.go feature-per-file naming pattern is consistently applied.
  • internal/mcp/: connection.go + http_transport.go split correctly isolates HTTP-specific transport complexity.
  • internal/cmd/flags_*.go: The getDefault*() helper pattern is intentional (documented in flags.go).
  • internal/launcher/log_helpers.go: 126-line logging helper file is tightly coupled to launcher state — appropriate placement.

Summary of Recommendations

Priority Finding File(s) Effort
🔴 High Merge tty/ micro-files container.gotty.go 10 min
🔴 High Remove single-function dockerutil package dockerutil/env.go → inline in mcp/connection.go 20 min
🟡 Medium Move TruncateSessionID to strutil auth/header.go:172strutil/truncate.go 20 min
🟡 Medium Rename transport.go to unified_server.go server/transport.go 15 min
🟡 Medium Extract config output from root.go root.goconfig_output.go 30 min
🟢 Low Merge flags_launch.go into flags.go cmd/flags_launch.go 10 min
🟢 Low Merge config_payload.go into config_core.go config/config_payload.go 15 min

Total Estimated Effort: ~2 hours

Implementation Checklist

  • Merge internal/tty/container.go into internal/tty/tty.go
  • Move dockerutil.ExpandEnvArgs inline to internal/mcp/connection.go; delete internal/dockerutil/ package
  • Move TruncateSessionID from internal/auth/header.go to internal/strutil/truncate.go; update callers
  • Rename internal/server/transport.gounified_server.go (verify logger var naming)
  • Extract writeGatewayConfig* from internal/cmd/root.go to internal/cmd/config_output.go
  • Merge internal/cmd/flags_launch.go into internal/cmd/flags.go
  • Merge internal/config/config_payload.go constants into config_core.go
  • Run make agent-finished after each change to verify no regressions

References:

Generated by Semantic Function Refactoring ·

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