[sergo] Sergo Report: errors-new-anti-pattern-and-interface-assertions - 2026-04-02 #24168
Closed
Replies: 1 comment
-
|
This discussion has been marked as outdated by Sergo - Serena Go Expert. A newer discussion is available at Discussion #24376. |
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.
-
Executive Summary
Today's Sergo analysis combined two complementary strategies: a deep extension of the compile-time interface assertion gap (first discovered in the mutex-safety run), and a novel scan for the
fmt.Errorf("%s", str)anti-pattern. The compile-time assertion gap is now fully mapped —ConditionNodehas 10 concrete implementors andCodingAgentEnginehas 4, all withoutvar _ Interface = (*Type)(nil)guards. The error construction anti-pattern was found in 6 locations acrosspkg/cliandpkg/workflow, where pre-formatted strings are wrapped withfmt.Errorfinstead of the more idiomatic and efficienterrors.New.Three actionable improvement tasks are generated below.
🛠️ Serena Tools Update
Tools Snapshot
Tool Capabilities Used Today
search_for_patternget_symbols_overviewagentic_engine.gofind_referencing_symbolsActionSHAResolverinterfaceread_file📊 Strategy Selection
Cached Reuse Component (50%)
Previous Strategy Adapted:
mutex-safety-and-compile-time-checks(2026-03-31, score 8/10)The prior run identified that no
var _ Interface = (*Type)(nil)compile-time assertions exist anywhere inpkg/. Today's run extends that finding to precisely quantify the two largest gaps:ConditionNode(10 concrete types inexpression_nodes.go) — not targeted in the prior runCodingAgentEngine(4 engine structs) — named in the prior run but not fully scopedModification: Pivoted from "what interfaces exist?" to "which have the most implementors and highest interface-change risk?"
CodingAgentEngineis a 9-method composite interface; a missed method implementation compiles fine at definition time but panics or silently misbehaves at registration.New Exploration Component (50%)
Novel Approach:
fmt.Errorf("%s", str)anti-pattern scanNo prior Sergo run scanned for this specific pattern. When a string has already been fully formatted via
fmt.Sprintf, wrapping it withfmt.Errorf("%s", prebuiltStr)is wasteful and unidiomatic. The correct idiom iserrors.New(prebuiltStr). The pattern was found in 6 production locations acrosspkg/cliandpkg/workflow.Hypothesis confirmed: The pattern clusters around validation code that builds rich error messages with
fmt.Sprintffirst, then converts to error — a common refactor artifact.Combined Strategy Rationale
Both components target correctness guarantees at the margin: compile-time assertions prevent silent interface drift;
errors.Newhygiene ensures the error construction path is as efficient and reviewable as possible. Both are low-risk fixes with zero behavior change.🔍 Analysis Execution
Codebase Context
pkg/workflow,pkg/cliexpression_nodes.go,agentic_engine.go, all validation filesFindings Summary
📋 Detailed Findings
Medium Priority Issues
Finding 1 —
fmt.Errorf("%s", str)Should Beerrors.New(str)(6 locations)In Go,
fmt.Errorfis intended for format verbs (%w,%v,%d, etc.) applied to runtime values. When the string has already been fully built — typically via a precedingfmt.Sprintf(...)call — the correct construction iserrors.New(prebuiltString). Usingfmt.Errorf("%s", prebuiltString)is flagged by theperfsprintlinter, incurs unnecessary format string parsing overhead, and obscures intent.All 6 occurrences follow the same pattern:
errMsg := fmt.Sprintf(...)immediately followed byreturn fmt.Errorf("%s", errMsg).pkg/cli/run_workflow_execution.goerrMsg(fromconsole.FormatErrorWithSuggestions)pkg/workflow/tools_validation.goerrMsg.String()(strings.Builder)pkg/workflow/engine_validation.goerrMsg(fromfmt.Sprintf)pkg/workflow/runtime_validation.goerrorMsg(fromfmt.Sprintf)pkg/workflow/permissions_validation.goerrorMsg.String()(strings.Builder)pkg/workflow/engine_definition.goerrMsg(fromfmt.Sprintf)Finding 2 — No Compile-Time Assertions for
ConditionNode(10 implementors)ConditionNodeis a single-method interface (Render() string) defined inexpression_nodes.go. It has 10 concrete implementations in the same file:ExpressionNode,AndNode,OrNode,NotNode,DisjunctionNode,FunctionCallNode,PropertyAccessNode,StringLiteralNode,BooleanLiteralNode,ComparisonNode. None have avar _ ConditionNode = (*T)(nil)guard.If a second method is ever added to
ConditionNode(e.g., for serialisation or type-tagging), all 10 implementors silently stop satisfying the interface. The break surfaces only when a value is assigned to aConditionNodevariable at runtime — which, for leaf nodes, may only happen deep inside expression parsing logic.Finding 3 — No Compile-Time Assertions for
CodingAgentEngine(4 engine implementors)CodingAgentEngineis the master composite interface inpkg/workflow/agentic_engine.go(line 230). It embeds all 9 sub-interfaces (Engine,CapabilityProvider,WorkflowExecutor,MCPConfigProvider,LogParser,SecurityProvider,ModelEnvVarProvider,AgentFileProvider,ConfigRenderer). Four concrete structs implement it by embeddingBaseEngine:ClaudeEngine(claude_engine.go)CopilotEngine(copilot_engine.go)GeminiEngine(gemini_engine.go)CodexEngine(codex_engine.go)BaseEngineprovides default implementations for all 9 sub-interfaces. If a new sub-interface is added toCodingAgentEngine, or ifBaseEnginedrops a method, the compiler will not catch the gap at the struct definition site — only when the struct is passed to a function acceptingCodingAgentEngine. Given the composite nature of this interface (9 sub-interfaces, ~30 methods), compile-time assertions are especially valuable here.✅ Improvement Tasks Generated
Task 1: Replace
fmt.Errorf("%s", str)witherrors.New(str)in 6 LocationsIssue Type: Error Construction Anti-Pattern
Problem:
Six locations use
fmt.Errorf("%s", prebuiltString)to construct an error from a fully-formed string. This is less idiomatic and slightly wasteful compared toerrors.New(str).Locations:
pkg/cli/run_workflow_execution.go:210pkg/workflow/tools_validation.go:358pkg/workflow/engine_validation.go:79pkg/workflow/runtime_validation.go:91pkg/workflow/permissions_validation.go:346pkg/workflow/engine_definition.go:280Impact:
perfsprintlinter will flag these; they also obscure intent for readersRecommendation:
For each location, replace
fmt.Errorf("%s", str)orfmt.Errorf("%s", builder.String())witherrors.New(str)orerrors.New(builder.String()). Ensure"errors"is imported (it may already be present; if not, add it and remove the now-unusedfmtimport if applicable).Before (example from
engine_validation.go:79):After:
Validation:
go build ./...go test ./pkg/...golangci-lint runno longer flagsperfsprinton these linesfmtimport removed in files where it is no longer used after the changeEstimated Effort: Small
Task 2: Add Compile-Time
ConditionNodeInterface Assertions for All 10 ImplementorsIssue Type: Compile-Time Interface Safety
Problem:
ConditionNodeinpkg/workflow/expression_nodes.gohas 10 concrete implementations, none withvar _ ConditionNode = (*T)(nil)guards. Interface drift (e.g., adding a second method toConditionNode) would only surface at assignment sites, not at implementation sites.Locations:
All in
pkg/workflow/expression_nodes.go:*ExpressionNode*AndNode*OrNode*NotNode*DisjunctionNode*FunctionCallNode*PropertyAccessNode*StringLiteralNode*BooleanLiteralNode*ComparisonNodeImpact:
Recommendation:
Add a block of compile-time assertions at the top of
expression_nodes.go(after imports, before the first type declaration):Validation:
go build ./pkg/workflow/...succeedsgo vet ./pkg/workflow/...cleanConditionNodegains a new method, each assertion line immediately shows the missing implementorEstimated Effort: Small
Task 3: Add Compile-Time
CodingAgentEngineAssertions for 4 Engine TypesIssue Type: Compile-Time Interface Safety
Problem:
The
CodingAgentEnginecomposite interface (9 sub-interfaces, ~30 methods) inpkg/workflow/agentic_engine.gois implemented by 4 concrete engine structs. None have compile-time assertions. Given the interface's composite nature, a missed method in any new sub-interface addition would be silently accepted until the engine is actually used.Locations:
pkg/workflow/claude_engine.go—*ClaudeEnginepkg/workflow/copilot_engine.go—*CopilotEnginepkg/workflow/gemini_engine.go—*GeminiEnginepkg/workflow/codex_engine.go—*CodexEngineImpact:
CodingAgentEnginepass the compiler silently;EngineRegistry.Registerwill catch it, but only at program startup, not at compile timeRecommendation:
Add a compile-time assertion block in
pkg/workflow/agentic_engine.goalongside the interface definition, or in each engine's own file. The centralized approach is preferred for discoverability:Place these assertions in a dedicated file (e.g.,
pkg/workflow/engine_assertions.go) to keep them visible and adjacent to the registration pattern.Validation:
go build ./pkg/workflow/...succeedsgo test ./pkg/workflow/...still passesBaseEnginetriggers a compile error at the assertion blockEstimated Effort: Small
📈 Success Metrics
This Run
Reasoning for Score
📊 Historical Context
Strategy Performance History
Cumulative Statistics
🎯 Recommendations
Immediate Actions
fmt.Errorf("%s", str)→errors.New(str)in 6 validation filesvar _ ConditionNode = (*T)(nil)block for 10 implementors inexpression_nodes.govar _ CodingAgentEngine = (*T)(nil)block for 4 engine typesAll three tasks are zero-behavior-change, compiler-verified fixes that improve long-term maintainability.
Long-term Improvements
perfsprintingolangci-lint) to catchfmt.Errorf("%s", ...)automatically on future PRsValidatableTool→*GitHubToolConfigandLogAnalysis→*FirewallAnalysis/*DomainAnalysispairs would also benefit from assertions (lower priority than the 10+4 above)🔄 Next Run Preview
Suggested Focus Areas
fmt.Errorflandscape with a%wvs%vcorrectness scan — the prior run foundcompiler_orchestrator_frontmatter.go:41using%vintentionally, but there may be unintentional cases in newer filescontext.Background()/context.TODO()usage inside non-main packages, which could indicate missing context propagation (this was partially identified in run 3 but not fully resolved)Strategy Evolution
The compile-time assertion theme has now been analyzed across three runs (mutex-safety → today). Future runs should consider closing out the remaining smaller interfaces (
ValidatableTool,LogAnalysis,ActionSHAResolver) in a single focused task, then move on to new domains.References:
Beta Was this translation helpful? Give feedback.
All reactions