🔧 Semantic Function Clustering Analysis
Repository: github/gh-aw · analyzed all non-test .go files under pkg/ · 2026-07-08
Overview
I inventoried every top-level function and method across pkg/ and clustered them by name/purpose to find duplicates and misplaced ("outlier") functions. Headline: the codebase is already well-factored. The naive "same function name in multiple files" signal is almost entirely false positives — the repeated names are intentional cross-package delegation shims into shared util packages, not copy-pasted logic. Each candidate below was verified by reading the actual function bodies.
The one genuinely actionable semantic finding is a small set of same-name / different-semantics unexported helpers across pkg/parser and pkg/workflow that invite confusion. Large-file / long-function splits are intentionally out of scope here because they are already tracked by the existing [file-diet] and [lint-monster] backlog issues.
Key metrics
| Metric |
Value |
Non-test .go files (pkg/) |
~1026 |
| Functions + methods cataloged |
~6,494 |
| Top-level package dirs |
30 |
| Cross-file same-name groups (raw) |
dominated by framework/build-tag noise |
| Verified problematic body duplicates |
0 |
| Actionable naming collisions |
2 |
Function concentration: pkg/workflow (2808 funcs / 419 files) + pkg/cli (1919 / 332) hold ~73% of all functions.
Why the duplicate signals are false positives (verified)
Every "duplicate" flagged by name-matching turned out to be a deliberate thin wrapper that forwards to a single shared implementation:
| Name |
Site A |
Site B |
Reality |
SanitizeName |
pkg/workflow/strings.go:97 |
pkg/stringutil/sanitize.go:76 |
workflow delegates → stringutil.SanitizeName |
GetVersion |
pkg/cli/version.go:11 |
pkg/workflow/version.go:34 |
cli delegates → workflow.GetVersion |
FindClosestMatches |
pkg/parser/schema_suggestions.go:231 |
pkg/stringutil/fuzzy_match.go:19 |
parser delegates → stringutil.FindClosestMatches |
compileSchema |
pkg/parser/schema_compiler.go:148 |
pkg/workflow/schema_utils.go:13 |
both delegate → parser.CompileSchema |
Additional noise classes excluded (build-tag twins, framework, fixtures)
_wasm.go build-tag twins: many exported names (FormatErrorMessage, RunGH, RenderTable, ExecGH, IsStderrTerminal, the Format*/Render* set, stderrWriter) are a foo.go + foo_wasm.go pair in the same package under different build constraints. Deduping these would break the wasm build.
- Framework conventions:
run(pass *analysis.Pass) appears in ~42 pkg/linters/* analyzers (go/analysis convention); init, New constructors.
pkg/linters/*/testdata/ fixtures: bad, good, doWork, suppressed, etc. are lint test inputs, not product code.
Because Go rejects two receiverless funcs with the same name in one package at compile time, there are no accidental same-package duplicates by construction.
The one actionable finding: same-name / different-semantics helpers
These unexported helpers share a name across pkg/parser and pkg/workflow but do different things with different signatures. They are not duplicates and not bugs — but grepping/navigating for them is misleading, and a future reader may assume they are interchangeable.
1. extractToolsFromFrontmatter
pkg/parser/content_extractor.go:60 → func(frontmatter map[string]any) (string, error) — merges tools and mcp-servers into a JSON string.
pkg/workflow/frontmatter_extraction_metadata.go:305 → func(frontmatter map[string]any) map[string]any — returns only the tools field via ExtractMapField.
- Recommendation: rename the workflow one to
extractToolsMapFromFrontmatter (it sits beside extractMCPServersFromFrontmatter/extractRuntimesFromFrontmatter, so a ...Map suffix reads naturally), or the parser one to extractToolsAndMCPJSON.
2. resolveImportInputPath
pkg/parser/import_input_substitution.go:47 → returns (string, bool) (formats resolved value to string).
pkg/workflow/expression_extraction.go:537 → returns (any, bool) (returns the raw resolved value).
- Recommendation: rename the workflow variant to
resolveImportInputValue for parity with the parser helper of the same name/behavior (parser.resolveImportInputValue already returns any).
Also-collides but lower value: hasCopilotRequestsWritePermission
pkg/cli/workflow_secrets.go:120 → func(frontmatter map[string]any) bool
pkg/workflow/permissions_operations.go:62 → func(workflowData *WorkflowData) bool
Different input types (raw frontmatter vs parsed WorkflowData), same intent. Acceptable as-is; consider aligning names only if these two ever need to agree on semantics.
Clustering / organization health
- Utility layering is good: shared logic lives in dedicated packages (
stringutil, fileutil, gitutil, typeutil, envutil, semverutil, importinpututil, ...) and the big packages call into them via the shims above. This is the desired end-state of a de-duplication effort — no action needed.
- Files are feature-named (
create_issue.go, create_pull_request.go, add_comment.go, codemod_*.go, compile_*.go), so the create*/codemod*/compile* clusters are correctly one-file-per-feature. ✓
- Scattered helper families (
build*/validate*/extract*/parse* span 40–64 files each in pkg/workflow and pkg/cli). This is expected spread for two 300–400-file feature packages and does not by itself indicate duplication. No consolidation recommended without body-level evidence, which I did not find.
Deliberately out of scope
Large files / long functions (e.g. pkg/cli/token_usage.go ~1128 LOC, pkg/workflow/compiler_activation_job_builder.go ~1110 LOC, pkg/cli/add_package_manifest.go ~984 LOC, pkg/cli/audit.go ~933 LOC) are already covered by the open [file-diet] and [lint-monster] function-length backlog issues. Not re-filed here to avoid duplication.
Next actions
Analysis metadata
- Method: full function inventory via AST-line extraction → name/receiver clustering → manual body verification of every duplicate candidate (delegation shims ruled out by reading source).
- Files analyzed: all non-test
.go under pkg/.
- Verified problematic duplicates: 0 · Naming collisions: 2 actionable + 1 optional.
Generated by 🔧 Semantic Function Refactoring · 487.7 AIC · ⌖ 15.2 AIC · ⊞ 9.1K · ◷
🔧 Semantic Function Clustering Analysis
Repository:
github/gh-aw· analyzed all non-test.gofiles underpkg/· 2026-07-08Overview
I inventoried every top-level function and method across
pkg/and clustered them by name/purpose to find duplicates and misplaced ("outlier") functions. Headline: the codebase is already well-factored. The naive "same function name in multiple files" signal is almost entirely false positives — the repeated names are intentional cross-package delegation shims into shared util packages, not copy-pasted logic. Each candidate below was verified by reading the actual function bodies.The one genuinely actionable semantic finding is a small set of same-name / different-semantics unexported helpers across
pkg/parserandpkg/workflowthat invite confusion. Large-file / long-function splits are intentionally out of scope here because they are already tracked by the existing[file-diet]and[lint-monster]backlog issues.Key metrics
.gofiles (pkg/)Function concentration:
pkg/workflow(2808 funcs / 419 files) +pkg/cli(1919 / 332) hold ~73% of all functions.Why the duplicate signals are false positives (verified)
Every "duplicate" flagged by name-matching turned out to be a deliberate thin wrapper that forwards to a single shared implementation:
SanitizeNamepkg/workflow/strings.go:97pkg/stringutil/sanitize.go:76stringutil.SanitizeNameGetVersionpkg/cli/version.go:11pkg/workflow/version.go:34workflow.GetVersionFindClosestMatchespkg/parser/schema_suggestions.go:231pkg/stringutil/fuzzy_match.go:19stringutil.FindClosestMatchescompileSchemapkg/parser/schema_compiler.go:148pkg/workflow/schema_utils.go:13parser.CompileSchemaAdditional noise classes excluded (build-tag twins, framework, fixtures)
_wasm.gobuild-tag twins: many exported names (FormatErrorMessage,RunGH,RenderTable,ExecGH,IsStderrTerminal, theFormat*/Render*set,stderrWriter) are afoo.go+foo_wasm.gopair in the same package under different build constraints. Deduping these would break the wasm build.run(pass *analysis.Pass)appears in ~42pkg/linters/*analyzers (go/analysis convention);init,Newconstructors.pkg/linters/*/testdata/fixtures:bad,good,doWork,suppressed, etc. are lint test inputs, not product code.Because Go rejects two receiverless funcs with the same name in one package at compile time, there are no accidental same-package duplicates by construction.
The one actionable finding: same-name / different-semantics helpers
These unexported helpers share a name across
pkg/parserandpkg/workflowbut do different things with different signatures. They are not duplicates and not bugs — but grepping/navigating for them is misleading, and a future reader may assume they are interchangeable.1.
extractToolsFromFrontmatterpkg/parser/content_extractor.go:60→func(frontmatter map[string]any) (string, error)— mergestoolsandmcp-serversinto a JSON string.pkg/workflow/frontmatter_extraction_metadata.go:305→func(frontmatter map[string]any) map[string]any— returns only thetoolsfield viaExtractMapField.extractToolsMapFromFrontmatter(it sits besideextractMCPServersFromFrontmatter/extractRuntimesFromFrontmatter, so a...Mapsuffix reads naturally), or the parser one toextractToolsAndMCPJSON.2.
resolveImportInputPathpkg/parser/import_input_substitution.go:47→ returns(string, bool)(formats resolved value to string).pkg/workflow/expression_extraction.go:537→ returns(any, bool)(returns the raw resolved value).resolveImportInputValuefor parity with the parser helper of the same name/behavior (parser.resolveImportInputValuealready returnsany).Also-collides but lower value:
hasCopilotRequestsWritePermissionpkg/cli/workflow_secrets.go:120→func(frontmatter map[string]any) boolpkg/workflow/permissions_operations.go:62→func(workflowData *WorkflowData) boolDifferent input types (raw frontmatter vs parsed
WorkflowData), same intent. Acceptable as-is; consider aligning names only if these two ever need to agree on semantics.Clustering / organization health
stringutil,fileutil,gitutil,typeutil,envutil,semverutil,importinpututil, ...) and the big packages call into them via the shims above. This is the desired end-state of a de-duplication effort — no action needed.create_issue.go,create_pull_request.go,add_comment.go,codemod_*.go,compile_*.go), so thecreate*/codemod*/compile*clusters are correctly one-file-per-feature. ✓build*/validate*/extract*/parse*span 40–64 files each inpkg/workflowandpkg/cli). This is expected spread for two 300–400-file feature packages and does not by itself indicate duplication. No consolidation recommended without body-level evidence, which I did not find.Deliberately out of scope
Large files / long functions (e.g.
pkg/cli/token_usage.go~1128 LOC,pkg/workflow/compiler_activation_job_builder.go~1110 LOC,pkg/cli/add_package_manifest.go~984 LOC,pkg/cli/audit.go~933 LOC) are already covered by the open[file-diet]and[lint-monster]function-length backlog issues. Not re-filed here to avoid duplication.Next actions
workflow.extractToolsFromFrontmatter→extractToolsMapFromFrontmatter(or parser side →extractToolsAndMCPJSON).workflow.resolveImportInputPath→resolveImportInputValuefor parity with the parser helper.hasCopilotRequestsWritePermissionhelpers only if their semantics ever need to converge.Analysis metadata
.gounderpkg/.