[sergo] Sergo Report: Multi-Return-Struct Refactoring - 2026-03-30 #23504
Closed
Replies: 1 comment
-
|
This discussion has been marked as outdated by Sergo - Serena Go Expert. A newer discussion is available at Discussion #23581. |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Overview
Today's Sergo analysis scanned 630 non-test Go files across 19 packages in
pkg/, targeting two complementary areas: (1) an extension of yesterday's structural anti-pattern analysis to cover multi-return-value functions that should be refactored into structs, and (2) a new deep exploration of runtime panics and context propagation gaps. Five concrete issues were found, yielding three high-quality improvement tasks. The codebase is overall healthy — the issues found are refactoring opportunities rather than bugs, but they meaningfully hurt call-site readability and long-term maintainability.Note: The Serena MCP language server was unavailable during this run (connection closed on first large result). Analysis was completed using bash/grep pattern tooling, which covered the same ground at the file-scan level but without LSP-powered semantic navigation. Serena's
search_for_patternwas partially used before the crash.Serena Tools Snapshot
Tools Used Today
search_for_patternfmt.Errorf %vscan (partial — crashed on large result)list_dirgrep -rnawkStrategy Selection
Cached Reuse Component (50%) — Extended Error & Panic Analysis
Previous Strategy Adapted:
error-patterns-first-run(2026-03-29, score 7/10)Yesterday's run found error-string anti-patterns (
%swrapping, timestamps in errors, swallowedos.Setenv). Today's run extends that lens to structural error propagation — specifically:context.Background()used where a parent context should be propagated (silent correctness issue)Modifications: Shifted from string-formatting errors to control-flow errors (panic vs error, context loss).
New Exploration Component (50%) — Multi-Return-Value Struct Refactoring
Novel Approach: Scan for functions returning 4+ values, especially unnamed ones.
Go idiom strongly prefers structs over long return lists: unnamed multi-returns force callers to track position, make it easy to discard important values silently with
_, and make signatures opaque in documentation. This is a gap not covered by previous runs.Tools Employed:
awkover all function signatures,grepfor blank-identifier caller patterns.Hypothesis: Large, complex files like
pkg/cli/audit_agentic_analysis.goandpkg/cli/logs_episode.golikely contain refactoring opportunities.Finding: Confirmed — discovered a 6-value return function with all callers using
_, _blank identifiers.Combined Strategy Rationale
Both components target the same root cause: implicit information loss. Error panics lose recoverability; context dropping loses cancellation propagation; unnamed multi-returns lose semantic meaning. Together they provide broad structural coverage of the codebase's implicit-loss patterns.
Codebase Context
pkg/pkg/cli/(audit, logs, episode),pkg/workflow/(strings, compiler)anyalias already adopted codebase-wide; zerointerface{}instances found)Findings Summary
Detailed Findings
High Priority Issues
[H1] 6-return-value function with silent discard at every call site
pkg/cli/audit_agentic_analysis.go:92deriveRunAgenticAnalysis(processedRun ProcessedRun, metrics LogMetrics) (*AwContext, []ToolUsageInfo, []CreatedItemReport, *TaskDomainInfo, *BehaviorFingerprint, []AgenticAssessment)_to discard 2 values each. Each caller discards a different pair — meaning the function returns data that no caller uses in full. This is a strong signal that the return type should be a struct.Medium Priority Issues
[M1] Unnamed 4-tuple return in
classifyEpisode/ 5-tuple inclassifyWorkflowCallEpisodepkg/cli/logs_episode.go:294andpkg/cli/logs_episode.go:358classifyEpisode(run RunData) (string, string, string, []string)— semantics are(episodeID, kind, confidence, reasons)classifyWorkflowCallEpisode(run RunData) (string, string, string, []string, bool)— same plusok boolstringwith no compiler-enforced ordering. A typo swappingkindandconfidenceis silently valid.[M2] Runtime panic in
GenerateHeredocDelimiterFromSeedwhen seed is emptypkg/workflow/strings.go:328seed == "", the function falls back tocrypto/rand. Ifcrypto/randfails (possible in low-entropy containers or restricted sandboxes), the compiler panics at workflow compilation time rather than returning an error. All current callers pass a non-empty seed (frontmatter hash), so this code path is currently unreachable — but the public API signature allows it.GenerateHeredocDelimiter(non-seeded) has the same panic and appears to be completely uncalled in the codebase (zero callers found outside its own file).[M3]
context.Background()used in 3 locations where parent context likely availablepkg/cli/resources.go:79—getRepoDefaultBranch(context.Background(), spec.RepoSlug)pkg/cli/includes.go:133—getRepoDefaultBranch(context.Background(), spec.RepoSlug)pkg/cli/update_workflows.go:225—getRepoDefaultBranch(context.Background(), repo)getRepoDefaultBranchwithcontext.Background(). The calling functions may have actxparameter propagated from the CLI command; usingcontext.Background()makes cancellation (e.g. Ctrl+C) ineffective for this HTTP call.getRepoDefaultBranchmakes a GitHub API call; if the user cancels, the call will hang until completion.Low Priority Issues
[L1] HTTP client at
pkg/cli/mcp_inspect_mcp.go:253has no.TimeoutfieldThe
http.Clientcreated with a customheaderRoundTrippertransport does not set.Timeout. However, the connection is bounded by acontext.WithTimeoutcall immediately after (line 261:context.WithTimeout(ctx, MCPConnectTimeout)). The context timeout provides equivalent protection for the connection phase, but subsequent reads during the MCP session are not bounded by the http.Client timeout. This is a low-risk gap since MCP sessions have their own protocol-level timeouts.Improvement Tasks
Task 1: Introduce
RunAgenticAnalysisResult StructIssue Type: Multi-Return-Value Refactoring
Problem:
deriveRunAgenticAnalysisreturns 6 values, and all 3 callers silently discard some using_. This makes the API hard to extend and forces positional knowledge at every call site.Location(s):
pkg/cli/audit_agentic_analysis.go:92— function definitionpkg/cli/logs_orchestrator.go:804— callerpkg/cli/audit.go:368— callerpkg/cli/audit_comparison.go:188— callerImpact:
Recommendation: Introduce a
RunAgenticAnalysisresult struct and update all callers.Before:
After:
Validation:
_blanks for this function remainEstimated Effort: Small
Task 2: Introduce
EpisodeClassificationStruct forclassifyEpisodeIssue Type: Multi-Return-Value Refactoring
Problem:
classifyEpisodereturns(string, string, string, []string)— four positional values (episodeID,kind,confidence,reasons) with no compiler-enforced semantic ordering. Three of the four arestring, meaning transposingkindandconfidencecompiles silently.Location(s):
pkg/cli/logs_episode.go:294—classifyEpisodepkg/cli/logs_episode.go:358—classifyWorkflowCallEpisodepkg/cli/logs_episode.go:84— call siteImpact:
kind/confidencestrings produces wrong episode graph edgesRecommendation: Introduce an
EpisodeClassificationstruct and update both functions.Before:
After:
Validation:
classifyEpisodeandclassifyWorkflowCallEpisodeupdatedEstimated Effort: Small
Task 3: Remove Dead
GenerateHeredocDelimiterand HardenFromSeedEmpty-Seed PathIssue Type: Dead Code + Runtime Panic Hardening
Problem:
GenerateHeredocDelimiter(non-seeded variant) inpkg/workflow/strings.go:293has zero callers outside of comments and its own file. It is effectively dead code that introduces a runtime panic path.GenerateHeredocDelimiterFromSeed(the live variant) falls back tocrypto/randwhenseed == "", triggering the same panic path. While all current callers pass a non-empty seed, the public API makes this reachable.Location(s):
pkg/workflow/strings.go:293-303—GenerateHeredocDelimiter(dead, panic path)pkg/workflow/strings.go:318-332—GenerateHeredocDelimiterFromSeedempty-seed panicImpact:
GenerateHeredocDelimiteror a caller that passes empty seed will panic in productionRecommendation:
GenerateHeredocDelimiter(it has no callers — confirmed by codebase-wide search).GenerateHeredocDelimiterFromSeed, replace thecrypto/randpanic fallback with a deterministic fallback using process PID + timestamp, or assert that seed must be non-empty with a descriptive panic message at the top of the function rather than silently in the middle.Before:
After:
This makes the contract explicit at the entry point (fail fast with a clear message) rather than failing silently deep inside a branch.
Validation:
GenerateHeredocDelimiterdeleted (verify zero callers first withgrep -r)GenerateHeredocDelimiterFromSeedupdated with guard clauseEstimated Effort: Small
Success Metrics
This Run
Score Reasoning
Historical Context
Strategy Performance
Cumulative Statistics
Recommendations
Immediate Actions
deriveRunAgenticAnalysistoRunAgenticAnalysisstruct — improves all 3 call sites and makes the API extensibleEpisodeClassificationstruct for episode classifier functions — eliminates positional ambiguity in string-heavy return tuplesGenerateHeredocDelimiterand add guard clause toGenerateHeredocDelimiterFromSeed— removes a latent runtime panic pathLong-term Improvements
pkg/cli/package has several functions with 5+ parameters that could benefit from option structs (e.g.RunWorkflowInteractivelyhas 8 parameters). This is a broader pattern worth a dedicated pass.context.Background()calls across 15 files suggest a systematic audit of context propagation from CLI entry points would be valuable.pkg/workflow/tools.go:enhanceToolDescription(378 lines) andapplyDefaults(316 lines) are candidates for function extraction in a future refactoring pass.Next Run Preview
Suggested Focus Areas
RunWorkflowInteractively,downloadWorkflowRunLogs, etc. all have 6-8 parameters; audit whether these share calling patterns that warrant a common options struct.find_referencing_symbolsto check whether any exported interfaces are only implemented once (possible over-abstraction) or have implementations that miss methods.Strategy Evolution
find_referencing_symbolsto validate dead-code findings (e.g. confirmGenerateHeredocDelimiterhas zero callers) before asserting them.References:
Beta Was this translation helpful? Give feedback.
All reactions