Analysis of 711 non-test Go files across pkg/ (workflow: 343, cli: 257, parser: 40, console: 25, others: 46) using Serena semantic analysis and function inventory.
Most of the codebase is well-organized: engine polymorphism (claude/codex/gemini each implement the same interface), WASM build-constraint alternates, the actionpins adapter layer, and the safe-outputs cluster (22 files) are all clean. Two actionable refactoring opportunities stand out.
Issue 1: Scattered Expression Detection Helpers
Four functions in pkg/workflow check whether a string is a GitHub Actions expression ($\{\{ ... }}), but they live in unrelated files and have slightly different implementations:
| Function |
File |
Logic |
Semantic |
isGitHubActionsExpression |
dispatch_repository_validation.go |
strings.Contains(s, "$\{\{") |
contains opening marker |
containsGitHubActionsExpression |
shell.go |
find $\{\{, then check }} follows |
contains complete expression |
isGitHubExpression |
safe_outputs_validation.go |
check both markers exist and $\{\{ index < }} index |
contains complete expression |
isExpressionString |
templatables.go |
HasPrefix("$\{\{") && HasSuffix("}}") |
whole string is an expression |
containsGitHubActionsExpression and isGitHubExpression are near-duplicates — both return true when a string contains a properly ordered $\{\{ ... }} pair, implemented independently in two unrelated source files.
isGitHubActionsExpression is a weaker variant (only checks for $\{\{), placing it two files away from the stronger check that should replace it.
isExpressionString has a distinct semantic (the entire string is an expression) but also belongs with the others.
A dedicated file pkg/workflow/expression_patterns.go already exists and holds all expression-related regex patterns. It is the correct consolidation point.
Recommendation
Move all four functions into pkg/workflow/expression_patterns.go, rationalizing into three semantically clear helpers:
// containsExpression reports whether s contains a $\{\{ ... }} expression.
func containsExpression(s string) bool { ... }
// isExpression reports whether the entire string s is a $\{\{ ... }} expression.
func isExpression(s string) bool { ... }
// hasExpressionMarker reports whether s contains the opening $\{\{ marker (permissive check).
func hasExpressionMarker(s string) bool { ... }
Then update the four call sites:
dispatch_repository_validation.go (4 call sites) → hasExpressionMarker or containsExpression
shell.go (1 call site) → containsExpression
safe_outputs_validation.go (1 call site) → containsExpression
templatables.go (6 call sites) → isExpression
Estimated effort: 1–2 hours
Benefit: single source of truth; easier to harden expression detection uniformly
Current implementations
// dispatch_repository_validation.go — only checks opening marker
func isGitHubActionsExpression(value string) bool {
return strings.Contains(value, "$\{\{")
}
// shell.go — finds $\{\{ then checks }} follows
func containsGitHubActionsExpression(s string) bool {
openIdx := strings.Index(s, "$\{\{")
if openIdx < 0 { return false }
return strings.Contains(s[openIdx:], "}}")
}
// safe_outputs_validation.go — both markers + ordering
func isGitHubExpression(s string) bool {
if !strings.Contains(s, "$\{\{") || !strings.Contains(s, "}}") { return false }
openIndex := strings.Index(s, "$\{\{")
closeIndex := strings.Index(s, "}}")
return openIndex >= 0 && closeIndex > openIndex+3
}
// templatables.go — whole string is expression
func isExpressionString(s string) bool {
return strings.HasPrefix(s, "$\{\{") && strings.HasSuffix(s, "}}")
}
Issue 2: Dual Version State Requires Manual Synchronization
pkg/cli/version.go and pkg/workflow/version.go each maintain their own version variable, kept in sync manually:
// pkg/cli/version.go
var version = "dev"
func GetVersion() string { return version }
func SetVersionInfo(v string) {
version = v
workflow.SetVersion(v) // manual sync
}
// pkg/workflow/version.go
var compilerVersion = "dev"
func GetVersion() string { return compilerVersion }
func SetVersion(v string) { compilerVersion = v }
If any code path calls workflow.SetVersion() without also calling cli.SetVersionInfo(), the two copies diverge silently. The cli.GetVersion() wrapper adds no behaviour beyond returning a local copy of a value that is already available via workflow.GetVersion().
Recommendation
Remove version from pkg/cli/version.go and have cli.GetVersion() delegate directly to workflow.GetVersion():
// pkg/cli/version.go — after refactor
func SetVersionInfo(v string) { workflow.SetVersion(v) }
func GetVersion() string { return workflow.GetVersion() }
This eliminates the duplicated state while preserving the public API of both packages.
Estimated effort: 30 minutes
Benefit: single source of truth for version; eliminates silent drift risk
Analysis Metadata
- Total non-test Go files analyzed: 711
- Packages covered: workflow (343), cli (257), parser (40), console (25), agentdrain (10), stringutil (6), plus 8 smaller packages
- Patterns confirmed as intentional (no action needed): WASM build-constraint file pairs, engine interface polymorphism (claude/codex/gemini/copilot/crush),
actionpins adapter layer with type aliases, safe-outputs 22-file cluster, update-entity helper family
- Detection method: Serena semantic analysis (LSP) + function name inventory + implementation comparison
- Workflow run: §24627930373
Generated by Semantic Function Refactoring · ● 542.3K · ◷
Analysis of 711 non-test Go files across
pkg/(workflow: 343, cli: 257, parser: 40, console: 25, others: 46) using Serena semantic analysis and function inventory.Most of the codebase is well-organized: engine polymorphism (claude/codex/gemini each implement the same interface), WASM build-constraint alternates, the
actionpinsadapter layer, and the safe-outputs cluster (22 files) are all clean. Two actionable refactoring opportunities stand out.Issue 1: Scattered Expression Detection Helpers
Four functions in
pkg/workflowcheck whether a string is a GitHub Actions expression ($\{\{ ... }}), but they live in unrelated files and have slightly different implementations:isGitHubActionsExpressiondispatch_repository_validation.gostrings.Contains(s, "$\{\{")containsGitHubActionsExpressionshell.go$\{\{, then check}}followsisGitHubExpressionsafe_outputs_validation.go$\{\{index <}}indexisExpressionStringtemplatables.goHasPrefix("$\{\{") && HasSuffix("}}")containsGitHubActionsExpressionandisGitHubExpressionare near-duplicates — both return true when a string contains a properly ordered$\{\{ ... }}pair, implemented independently in two unrelated source files.isGitHubActionsExpressionis a weaker variant (only checks for$\{\{), placing it two files away from the stronger check that should replace it.isExpressionStringhas a distinct semantic (the entire string is an expression) but also belongs with the others.A dedicated file
pkg/workflow/expression_patterns.goalready exists and holds all expression-related regex patterns. It is the correct consolidation point.Recommendation
Move all four functions into
pkg/workflow/expression_patterns.go, rationalizing into three semantically clear helpers:Then update the four call sites:
dispatch_repository_validation.go(4 call sites) →hasExpressionMarkerorcontainsExpressionshell.go(1 call site) →containsExpressionsafe_outputs_validation.go(1 call site) →containsExpressiontemplatables.go(6 call sites) →isExpressionEstimated effort: 1–2 hours
Benefit: single source of truth; easier to harden expression detection uniformly
Current implementations
Issue 2: Dual Version State Requires Manual Synchronization
pkg/cli/version.goandpkg/workflow/version.goeach maintain their own version variable, kept in sync manually:If any code path calls
workflow.SetVersion()without also callingcli.SetVersionInfo(), the two copies diverge silently. Thecli.GetVersion()wrapper adds no behaviour beyond returning a local copy of a value that is already available viaworkflow.GetVersion().Recommendation
Remove
versionfrompkg/cli/version.goand havecli.GetVersion()delegate directly toworkflow.GetVersion():This eliminates the duplicated state while preserving the public API of both packages.
Estimated effort: 30 minutes
Benefit: single source of truth for version; eliminates silent drift risk
Analysis Metadata
actionpinsadapter layer with type aliases, safe-outputs 22-file cluster, update-entity helper family