🔧 Semantic Function Clustering Analysis
Repository: github/gh-aw · non-test .go files under pkg/ · analysis date 2026-07-12
Executive summary
Analyzed 1,057 non-test Go files (~5,141 free functions) across pkg/. The good news dominates: the codebase already follows a strong one-file-per-feature convention (e.g. codemod_*.go, logs_*.go, compile_*.go, outcome_eval_*.go), so there are almost no "function in the wrong file" outliers, and every same-name collision within a package is a deliberate //go:build platform pair, not accidental duplication.
Two actionable items surfaced after semantic verification:
| # |
Finding |
Severity |
Where |
| 1 |
Pure path/string helpers duplicated & drifting between the wasm and non-wasm builds |
Medium (correctness risk) |
pkg/parser/remote_fetch_wasm.go |
| 2 |
Scattered render* / build* / format* / normalize* helpers with no home file |
Low (organization) |
pkg/cli, pkg/workflow |
Detection method: ripgrep-based function inventory + name clustering, then Serena/manual read-verification of candidate pairs.
Finding 1 — Drifting duplicate helpers across build tags (actionable)
pkg/parser splits platform code into remote_resolve_path.go (//go:build !js && !wasm) and remote_fetch_wasm.go (//go:build js || wasm). Several helpers in the wasm file are pure path/string logic with no platform-specific reason to differ, yet they are hand-reimplemented — and have already drifted:
isUnderWorkflowsDirectory — canonical remote_resolve_path.go:14 uses constants.WorkflowsDirSlash; wasm copy remote_fetch_wasm.go:11 hardcodes the literal ".github/workflows/".
isCustomAgentFile — canonical uses constants.AgentsDir; wasm copy remote_fetch_wasm.go:23 hardcodes ".github/agents/".
isRepositoryImport — canonical uses strings.Cut; wasm copy remote_fetch_wasm.go:28 uses the older strings.Index slicing form.
IsWorkflowSpec — remote_workflow_spec.go:14 vs remote_fetch_wasm.go:124; same intent, minor variation.
Why this matters: these are not stubs. If the .github/workflows/ path constant ever changes, the wasm build silently keeps the old value → divergent behavior only on the wasm target, which is exactly the case tests miss most easily.
Recommendation: move the platform-independent helpers into a build-tag-neutral file (e.g. pkg/parser/remote_path.go with no //go:build constraint) and delete both copies. Keep only the genuinely platform-specific functions build-tagged (ResolveIncludePath, remote-fetch I/O). This removes ~4 duplicated functions and the drift class entirely.
Confirmed distinct — do NOT merge (legitimate platform stubs)
These same-name pairs are correct as-is; listed so a mechanical "dedupe by name" pass doesn't collapse them:
pkg/workflow: RunGH/ExecGH/... (github_cli.go real vs github_cli_wasm.go → returns "gh CLI not available in Wasm"), findGitRoot (git_helpers_wasm.go → "."), isDockerDaemonRunning (docker_validation_wasm.go → false).
pkg/colorwriter: New/Stderr (real writer vs wasm passthrough).
pkg/tty: IsStdoutTerminal/IsStderrTerminal.
pkg/console: the Format*/Render* family (console.go vs console_wasm.go) — wasm twin intentionally strips TTY styling + logging. Could optionally share a style-free core, but the split is defensible; lower priority than Finding 1.
Finding 2 — Scattered helpers with no home file (organization)
The repo already models the desired pattern in pkg/workflow (parse_helpers.go, validation_helpers.go). Several helper families have grown without an equivalent home:
Helper-prefix scatter (free functions, ≥8 occurrences, no dedicated file)
pkg/cli — has only a generic helpers.go; no topic helper files:
| Prefix |
occurrences |
files spread |
home file? |
render* |
95 |
25 |
none |
build* |
89 |
49 |
none |
parse* |
64 |
46 |
none |
format* |
33 |
20 |
none |
normalize* |
19 |
18 |
none |
pkg/workflow — parse*/validate* already have homes; these do not:
| Prefix |
occurrences |
files spread |
home file? |
format* |
29 |
22 |
none |
normalize* |
28 |
25 |
none |
render* |
20 |
13 |
none |
Recommendation (low priority, incremental): when touching these areas, consolidate obvious cross-file helpers into format_helpers.go / render_helpers.go / normalize_helpers.go, mirroring the existing parse_helpers.go convention. This is discoverability-driven, not a duplication bug — do it opportunistically, not as a big-bang move.
What was checked and found clean
- Outliers (functions in the wrong file): none material. The
*_command.go / feature-name file convention is followed consistently across pkg/cli and pkg/workflow.
- Accidental in-package name collisions: zero — all 53 same-name groups are
//go:build platform pairs.
- Cross-package "same purpose" duplication: dedicated
*util packages (stringutil, fileutil, sliceutil, setutil, ...) already centralize generic helpers.
Suggested next actions
Analysis metadata
- Non-test Go files analyzed: 1,057 · free functions cataloged: ~5,141
- Same-name groups: 53 (all build-tag pairs) · confirmed actionable duplicates: ~4 (
pkg/parser wasm)
- Method: ripgrep function inventory + name clustering + read-verification of candidate pairs
Generated by 🔧 Semantic Function Refactoring · 745.7 AIC · ⌖ 15 AIC · ⊞ 9.1K · ◷
🔧 Semantic Function Clustering Analysis
Repository:
github/gh-aw· non-test.gofiles underpkg/· analysis date 2026-07-12Executive summary
Analyzed 1,057 non-test Go files (~5,141 free functions) across
pkg/. The good news dominates: the codebase already follows a strong one-file-per-feature convention (e.g.codemod_*.go,logs_*.go,compile_*.go,outcome_eval_*.go), so there are almost no "function in the wrong file" outliers, and every same-name collision within a package is a deliberate//go:buildplatform pair, not accidental duplication.Two actionable items surfaced after semantic verification:
pkg/parser/remote_fetch_wasm.gorender*/build*/format*/normalize*helpers with no home filepkg/cli,pkg/workflowDetection method: ripgrep-based function inventory + name clustering, then Serena/manual read-verification of candidate pairs.
Finding 1 — Drifting duplicate helpers across build tags (actionable)
pkg/parsersplits platform code intoremote_resolve_path.go(//go:build !js && !wasm) andremote_fetch_wasm.go(//go:build js || wasm). Several helpers in the wasm file are pure path/string logic with no platform-specific reason to differ, yet they are hand-reimplemented — and have already drifted:isUnderWorkflowsDirectory— canonicalremote_resolve_path.go:14usesconstants.WorkflowsDirSlash; wasm copyremote_fetch_wasm.go:11hardcodes the literal".github/workflows/".isCustomAgentFile— canonical usesconstants.AgentsDir; wasm copyremote_fetch_wasm.go:23hardcodes".github/agents/".isRepositoryImport— canonical usesstrings.Cut; wasm copyremote_fetch_wasm.go:28uses the olderstrings.Indexslicing form.IsWorkflowSpec—remote_workflow_spec.go:14vsremote_fetch_wasm.go:124; same intent, minor variation.Why this matters: these are not stubs. If the
.github/workflows/path constant ever changes, the wasm build silently keeps the old value → divergent behavior only on the wasm target, which is exactly the case tests miss most easily.Recommendation: move the platform-independent helpers into a build-tag-neutral file (e.g.
pkg/parser/remote_path.gowith no//go:buildconstraint) and delete both copies. Keep only the genuinely platform-specific functions build-tagged (ResolveIncludePath, remote-fetch I/O). This removes ~4 duplicated functions and the drift class entirely.Confirmed distinct — do NOT merge (legitimate platform stubs)
These same-name pairs are correct as-is; listed so a mechanical "dedupe by name" pass doesn't collapse them:
pkg/workflow:RunGH/ExecGH/... (github_cli.goreal vsgithub_cli_wasm.go→ returns "gh CLI not available in Wasm"),findGitRoot(git_helpers_wasm.go→"."),isDockerDaemonRunning(docker_validation_wasm.go→false).pkg/colorwriter:New/Stderr(real writer vs wasm passthrough).pkg/tty:IsStdoutTerminal/IsStderrTerminal.pkg/console: theFormat*/Render*family (console.govsconsole_wasm.go) — wasm twin intentionally strips TTY styling + logging. Could optionally share a style-free core, but the split is defensible; lower priority than Finding 1.Finding 2 — Scattered helpers with no home file (organization)
The repo already models the desired pattern in
pkg/workflow(parse_helpers.go,validation_helpers.go). Several helper families have grown without an equivalent home:Helper-prefix scatter (free functions, ≥8 occurrences, no dedicated file)
pkg/cli— has only a generichelpers.go; no topic helper files:render*build*parse*format*normalize*pkg/workflow—parse*/validate*already have homes; these do not:format*normalize*render*Recommendation (low priority, incremental): when touching these areas, consolidate obvious cross-file helpers into
format_helpers.go/render_helpers.go/normalize_helpers.go, mirroring the existingparse_helpers.goconvention. This is discoverability-driven, not a duplication bug — do it opportunistically, not as a big-bang move.What was checked and found clean
*_command.go/ feature-name file convention is followed consistently acrosspkg/cliandpkg/workflow.//go:buildplatform pairs.*utilpackages (stringutil,fileutil,sliceutil,setutil, ...) already centralize generic helpers.Suggested next actions
isUnderWorkflowsDirectory,isCustomAgentFile,isRepositoryImport(andIsWorkflowSpecif identical) into a build-tag-neutralpkg/parser/remote_path.go; delete the drifted wasm copies. Est. ~1–2h; removes a latent wasm-only correctness bug.format_helpers.go/render_helpers.goinpkg/cliandpkg/workflowas those areas are touched.Analysis metadata
pkg/parserwasm)