Repository navigation
[typist] Typist: Go Type Consistency Analysis (2026-10-06) #66107
Closed
Replies: 1 comment
|
This discussion was automatically closed because it expired on 2026-10-07T11:42:17.358Z.
|
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
Executive Summary
I scanned all 1,428 non-test
.gofiles underpkg/for duplicated type definitions and weakly-typed (interface{}/any/untyped constant) usage. The good news first: for a codebase this size, exact type-name duplication is rare — out of 1,360 top-level type declarations (structs, interfaces, aliases), only 3 names recur outside of test files, and one of those is the project's intentional WASM-stub pattern (not a real duplicate). The two real duplicate clusters are both cases of the same parsing/schema logic being reimplemented in a second package instead of shared.The more interesting findings are on the untyped-usage side.
map[string]anyand[]anyare everywhere (~3,578 occurrences) but that's expected and mostly fine for a YAML/JSON frontmatter compiler walking arbitrary parsed documents — the vast majority are guarded by safe type assertions. The handful ofany-typed struct fields are where the real risk lives: a few of them (notably a security-relevant MCP Gateway policy field) are read back with raw type assertions or an unchecked==comparison rather than comma-ok checks, which is worth tightening up even though nothing is on fire today.Full Analysis Report
Duplicated Type Definitions
Summary Statistics
Cluster 1:
runtimeImportReference— parallel macro parsersType: Near duplicate
Occurrences: 2
Impact: High — same markdown macro parsed independently in two packages
Locations:
pkg/parser/frontmatter_hash.go:572—type runtimeImportReference struct { path string; startLine int; endLine int }pkg/workflow/runtime_import_validation.go:38—type runtimeImportReference struct { importPath string; startLine int; endLine int }Definition Comparison:
Recommendation:
pkg/parser), withpkg/workflowimporting it.{{#runtime-import}}macro grammar instead of two that can drift apart.Cluster 2:
graderManifestEntry— writer/reader schema drift riskType: Near duplicate
Occurrences: 2
Impact: Medium — independently maintained read/write sides of the same JSON contract
Locations:
pkg/workflow/compiler_yaml_graders.go:86— the writer: full 14-field struct (ID, Name, Description, Source, Enabled, Unit, Direction, Threshold, Max, Min, Digest, Run, Inline, Config), serialized into the compiled workflow.pkg/cli/audit_report_graders.go:67— the reader: a separately declared 5-field subset (ID, Name, Unit, Direction, Threshold).Recommendation:
pkg/types) imported by both thepkg/workflowwriter and thepkg/clireader, so the on-disk JSON contract has one authoritative Go definition.Noted but not actionable:
ProgressBar/SpinnerWrapperpkg/console/progress.govspkg/console/progress_wasm.go, andpkg/console/spinner.govspkg/console/spinner_wasm.go, each redeclare the same type name under mutually exclusive(go/redacted):build js || wasmvs(go/redacted):build !js && !wasmtags, with explicit cross-references in comments. This is the repository's documented WASM-stub convention — listed here only so it isn't mistaken for an uncontrolled duplicate.Untyped Usages
Summary Statistics
interface{}usages: 45 (nearly all in linter testdata fixtures that intentionally demonstrate the anti-pattern)anyusages: ~4,900 (dominated bymap[string]any~3,057 and[]any~521, idiomatic for walking parsed YAML/JSON frontmatter)Category 1:
anyin Struct Fields (highest impact)Impact: High — several of these are read back with unchecked type assertions
Example 1:
PrivateToPublicFlows any— security policy fieldpkg/workflow/tools_types.go:451PrivateToPublicFlows anyyaml:"-"``string("allow") or[]string(MCP server IDs), asserted across 4 files:pkg/workflow/mcp_github_config.go:721(== "allow"raw equality against anany, no ok-check),pkg/workflow/mcp_gateway_config.go:207(type switch),pkg/workflow/strict_mode_network_validation.go:235,256(.(string)/.([]string)),pkg/workflow/strict_mode_private_to_public_flows_validation.go:19.== "allow"comparison atmcp_github_config.go:721compares ananydirectly to a string literal — it silently evaluatesfalsefor any non-string value instead of failing loudly. A sum type would centralize validation in one unmarshal step instead of 4 scattered assertions.Example 2: Duplicated
Raw anymemory-tool patternpkg/workflow/tools_types.go:499,505,512andpkg/workflow/repo_memory.go:87—CacheMemoryToolConfig,DriveMemoryToolConfig,CommentMemoryToolConfig,RepoMemoryToolConfigeach declare an identicalRaw anyfield for a "bool | array | map" shape, each with its own type-switch (pkg/workflow/cache_config.go:306-334,drive_memory_config.go:195,comment_memory.go:40,repo_memory.go:108-114).cache_config.go:334(and the equivalent branches elsewhere), a value that's not bool/array/map silently falls through toreturn nil, nil— treating a malformed config as absent rather than raising a validation error.ParseBoolOrArrayOrMap(v any) (ToolConfigShape, error)) used by all four sites, returning an explicit error for unrecognized shapes instead of silently dropping the config.Example 3:
Target anyacross 5 Azure DevOps safe-output configspkg/workflow/safe_outputs_azure_devops.go:40,52,57,64,70(UpdateWorkItemConfig,CommentOnWorkItemConfig,AssignWorkItemConfig,LinkWorkItemsConfig,UploadWorkItemAttachmentConfig)..Target's shape — it's parsed from frontmatter and passed straight through to the generated workflow/runtime script.Example 4:
NewConfig func() any— widest-reach instance (~54 call sites)pkg/workflow/safe_output_handlers.go:19, set by ~54 handler entries (e.g. lines 28, 34, 40, ..., 662).pkg/workflow/safe_outputs_tools_repo_params.go:44andsafe_outputs_permissions.go:242(reflect.ValueOf+AssignableTobeforefield.Set), already guarded against panics with a logged failure at line 762.type SafeOutputConfig interface { isSafeOutputConfig() }implemented by all ~54 config structs, changing the factory toNewConfig func() SafeOutputConfig. Lower urgency than the others since the existing reflection check prevents a panic — but a compile-time guarantee would catch a mis-registered factory immediately instead of at runtime.Example 5:
Engine anyonFrontmatterConfigpkg/workflow/frontmatter_types.go:356, documented as a deliberate workaround for JSON unmarshal failures (string-or-object engine spec).EngineSpectype with a customUnmarshalJSONaccepting either a bare string or an object, preserving the documented flexibility withoutany. No runtime risk today (the field is only nil-checked and passed through), but any future direct read offc.Enginehas no compile-time shape hint.Category 2: Untyped Constants
Impact: Medium — comparisons compile regardless of typos, and intent is implicit
Example: workflow run phase strings
Why: the return value is compared via
==at 10+ call sites across 5 engine files (claude_engine.go,codex_engine.go,copilot_engine_execution.go,gemini_engine.go,pi_engine.go). A named type lets linters flag an exhaustiveness gap when a new phase is added, and prevents a typo'd literal from compiling silently.Lower-priority / acceptable-as-is (included for completeness)
pkg/cli/mcp_tools_privileged.go:458—RunID any/RunIDOrURL anyonauditArgs, consumed bynormalizeAuditRunInputwhich type-switches exhaustively (nil/string/float64/int/int64) with a clear error default. Driven by an untrusted JSON-RPC boundary; already defensively written — polish only.pkg/cli/add_interactive_engine.go:238—authMethodCopilotRequests/authMethodPATstring constants, compared only within one file. Small, self-contained two-value enum; a representative "should be a named type" example but not a current source of risk.What Was Deliberately Excluded
map[string]any/[]anyfor walking parsed YAML/JSON (~3,578 occurrences across 477+ files) — idiomatic for a frontmatter compiler, almost always guarded by safe type assertions.pkg/typeutil/convert.go/pkg/typeutil/lookup.gogeneric coercion helpers (ConvertToInt,ConvertToFloat,LookupMap, ~50 call sites) — explicitly documented, safe-switch generic helpers.pkg/linters/*/testdata/— synthetic lint-violation samples, not real code (even though their filenames don't end in_test.go).🎯 What Should We Do About This?
Here's a suggested action plan, prioritized by impact:
Priority 1: High —
PrivateToPublicFlows anysecurity fieldSteps: define
PrivateToPublicFlowsPolicywith a customUnmarshalYAML; replace the 4 call-site assertions (mcp_github_config.go:721,mcp_gateway_config.go:207,strict_mode_network_validation.go:235,256,strict_mode_private_to_public_flows_validation.go:19); run tests.Estimated effort: 2-3 hours · Impact: High — closes an unchecked equality comparison on a security-relevant field.
Priority 2: High — consolidate
runtimeImportReferencemacro parsingSteps: pick one home (likely
pkg/parser), move the struct/regex/extraction function there, updatepkg/workflowto import it, delete the duplicate.Estimated effort: 2-4 hours · Impact: High — removes a second independently-maintained implementation of the same macro grammar.
Priority 3: Medium — shared memory-tool config parsing (
Raw anyx4) + validation gapSteps: write
ParseBoolOrArrayOrMaphelper with an explicit error path; replace the 4 duplicated type-switches; add a test for the "unrecognized shape" case that currently silently returnsnil, nil.Estimated effort: 3-4 hours · Impact: Medium — fixes a real silent-failure gap, once instead of four times.
Priority 4: Medium — unify
graderManifestEntryreader/writer schemaSteps: move the schema to
pkg/types; update bothpkg/workflow(writer) andpkg/cli(reader) imports.Estimated effort: 2-3 hours · Impact: Medium — prevents future writer/reader drift.
Priority 5: Low — type
RunPhase, Azure DevOpsTarget,SafeOutputConfigmarker interfaceLower urgency polish items; tackle opportunistically alongside nearby work.
Implementation Checklist
PrivateToPublicFlows anywith a typed policy + safe unmarshalruntimeImportReferenceparsing into one packageRaw anyswitches)graderManifestEntryschema between writer (pkg/workflow) and reader (pkg/cli)run_phase.goconstants asRunPhaseTarget anyshape at parse timeSafeOutputConfigmarker interface for the 54-siteNewConfig func() anyfactoryAnalysis Metadata
pkg/pkg/**/*.go, with manual reference tracing for each candidateReferences:
All reactions