[typist] π€ Typist β Go Type Consistency Analysis (github/gh-aw) #42745
Closed
Replies: 1 comment
-
|
This discussion has been marked as outdated by Typist - Go Type Analysis. A newer discussion is available at Discussion #42949. |
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.
-
π€ Typist β Go Type Consistency Analysis
Analysis of repository: github/gh-aw Β· 2026-07-01
Executive Summary
Good news first: this codebase is already quite type-disciplined. I swept every non-test
.gofile underpkg/(986 files, 719 type definitions) hunting for the two classic type-safety smells β the same type defined in multiple places, andinterface{}/anywhere a concrete type belongs β and the headline is that there are essentially zero genuine cross-package duplicate types. Every name that looks duplicated turned out to be a deliberate, healthy pattern: type aliases that re-export a single source of truth (type LogMetrics = workflow.LogMetrics),//go:build js || wasmbuild-tag variants of the same type, or linter test fixtures undertestdata/. That is single-source-of-truth done correctly, and it is worth saying so plainly.The one substantive opportunity is untyped data. The project leans heavily on
any(2,824 occurrences vs. a single lingeringinterface{}), and the dominant shape ismap[string]any(2,159 lines, concentrated inpkg/workflowandpkg/cli). Most of that is the deliberate YAML/JSON frontmatter boundary β the dedicatedpkg/typeutilpackage exists precisely to convert those heterogeneous values safely, so it is a conscious design, not carelessness. But it has grown a long tail: 134 function signatures takefrontmatter map[string]anyand hand-walk it with ~1,196 type assertions inpkg/workflowalone. Introducing a typedFrontmatterstruct at that boundary is the single highest-leverage refactor available β everything else is polish.Full Analysis Report
Duplicated Type Definitions
Summary Statistics
The clustering by type name surfaced 14 candidate groups. On inspection all 14 are intentional, in three benign categories.
Category A β Type aliases (single source of truth, already correct β )
These use Go's
type X = pkg.Xalias syntax to re-export one canonical definition β exactly the shared-types pattern a naive report would recommend.pkg/workflow/action_pins.goβActionYAMLInput,ActionPin,ContainerPin,ActionPinsData,SHAResolverpkg/actionpins/actionpins.gopkg/workflow/inputs.goβInputDefinitionpkg/types/input_definition.gopkg/workflow/strings.goβSanitizeOptionspkg/stringutil/sanitize.gopkg/cli/logs_models.goβLogMetrics,ToolCallInfopkg/workflow/metrics.goRecommendation: none β keep as is.
Category B β Build-tag variants (
//go:build js || wasm, legitimate β )Same type name, mutually-exclusive build constraints, same package β alternative wasm implementations, not duplicates.
ProgressBarβpkg/console/progress.govsprogress_wasm.goSpinnerWrapperβpkg/console/spinner.govsspinner_wasm.goRepositoryFeaturesβpkg/workflow/repository_features_validation.govs_wasm.goRecommendation: none.
Category C β Linter test fixtures (out of scope π«)
type Workerappears 3Γ β all underpkg/linters/*/testdata/src/.... Analyzer inputs, excluded by the skip-testdata constraint.Structural near-duplicates
The codebase defines 166 distinct
*Configtypes and 45 distinct*Optionstypes. I checked whether these hide structural clones (β₯80% shared fields). They do not in any material way β each*Configmaps to a distinct feature surface (safe-outputs, engine, rate-limit, ...). Healthy domain modeling, not duplication. No consolidation recommended.Untyped Usages
Summary Statistics
interface{}usages: 1anyusages: 2,824map[string]anylines: 2,159 (233 inpkg/workflow, 110 inpkg/cli, 19 inpkg/parser)[]any/[]interface{}lines: 416frontmatter map[string]anyfunction signatures: 134pkg/workflow: ~1,196Category 1 β The
frontmatter map[string]anyboundary (High impact π―)This is the finding worth acting on. YAML frontmatter is parsed into
map[string]any, then 134 functions navigate it with nested type-assertion ladders. Representative example βpkg/workflow/role_checks.go:100:Dozens of sibling
extractRoles,extractBots,extractRateLimitConfig,needsRoleCheck(... frontmatter map[string]any)functions re-derive structure the YAML schema already knows. Cost: no compile-time guarantee"on"is a map, silentnilon shape mismatch, ~1,196 assertions to keep correct.Suggested direction β define the schema once and unmarshal into it:
on.*), not in one sweep.extractXfamily.Category 2 β The lone
interface{}(Trivial π§Ή)Exactly one raw
interface{}remains and it is alreadyanyin spirit:pkg/workflow/compiler_types.go:746βReportFailureAsIssue anyβ the doc comment still mentions[]interface{}. The field legitimately holdsbool | string | []categories, soanyis correct; only the comment references the old spelling. Recommendation: keep the field; update the comment, and add a lint rule sointerface{}does not creep back (the repo has effectively standardized onany).Category 3 β Untyped constants & the
EngineNamenear-miss (Low impact)pkg/constantsis exemplary β it already defines semantic string types and uses them (pkg/constants/engine_constants.go):One small inconsistency in that same file: the type exists but adjacent structures fall back to plain
string:EngineOption.Value string(~line 47) andfunc GetEngineOption(engineValue string)(line 164) could takeEngineName, erasing thestring(CopilotEngine)conversions.AgenticEngines []string(line 43) is built viastring(ClaudeEngine)...; could be[]EngineName.The secret-name constants (
AnthropicAPIKey = "ANTHROPIC_API_KEY", ...) andEnvVar*constants are untyped strings; atype SecretName string/type EnvVarName stringwould make the many functions passing them around self-documenting β optional polish, not a safety fix.Benefit: removes a handful of
string(...)conversions; engine/secret identifiers stop being interchangeable with arbitrary strings.Effort: 1β2 hours, low risk.
Refactoring Recommendations (prioritized)
Priority 1 β Typed frontmatter at the parse boundary (High)
Define a
Frontmatterstruct (with inlineExtra map[string]anyescape hatch) and migrate theextractX(frontmatter map[string]any)family inpkg/workflowone group at a time, starting withon.*inrole_checks.go. Each conversion is an independent PR. Impact: High β eliminates the bulk of ~1,196 hand assertions. Effort: Large (incremental).Priority 2 β Tighten
EngineNameusage (Low)Change
EngineOption.Value,GetEngineOption's parameter, andAgenticEnginestoEngineName; considerSecretName/EnvVarNametypes. Impact: LowβMedium. Effort: 1β2 hours.Priority 3 β Standardize on
any, retire the lastinterface{}(Trivial)Update the stale comment at
compiler_types.go:746; add a lint/gofmt -r 'interface{} -> any'guard. Impact: Cosmetic/consistency. Effort: <30 min.Implementation Checklist
Frontmatter/OnConfigstruct and migraterole_checks.goon.*extractors as a reference PRextractX(frontmatter map[string]any)families incrementallyEngineOption.Value/GetEngineOption/AgenticEnginestoEngineNameSecretName/EnvVarNamesemantic types inpkg/constantscompiler_types.go:746comment; add aninterface{}βanyguardgo build ./... && go test ./...after each stepAnalysis Metadata
testdata/)any/ 1interface{}/ 2,159map[string]anyget_symbols_overviewguidance, pattern extraction, and manual verification of every reported clusterBeta Was this translation helpful? Give feedback.
All reactions