[typist] Typist - Go Type Consistency Analysis #33748
Closed
Replies: 1 comment
-
|
This discussion was automatically closed because it expired on 2026-05-22T12:32:45.078Z.
|
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.
-
Executive Summary
This analysis scanned 821 non-test Go files across
pkg/and identified 722 struct type definitions, 588 function signatures acceptingany, and 1,815 uses ofmap[string]any. Rawinterface{}is effectively absent (0 hits) — the codebase has consistently migrated toany.The dominant type-safety gap is YAML/JSON frontmatter parsing pipelines in
pkg/workflow/that threadanyandmap[string]anythrough many layers before crystallising into typed configs. The dominant duplication gap is the*Configfamily (148 Config types) — many use embedding or aliases intentionally, but several near-duplicate clusters could share base types. A handful of documented alias patterns (e.g.CloseEntityConfig,IssueReportingConfig) are deliberate and should be left alone.Full Analysis Report
Duplicated Type Definitions
Summary Statistics
Cluster 1: MCP Server Config Family (near-duplicates with base embedding)
Severity: High — three layers of MCP config diverge despite a shared base.
Locations:
pkg/types/mcp.go:6—BaseMCPServerConfig(Command, Args, Env, Type, Version, URL, Headers, Auth, Container, Entrypoint, EntrypointArgs, Mounts)pkg/parser/mcp.go:38—RegistryMCPServerConfigembeds Base; adds Name, Registry, ProxyArgs, Allowedpkg/workflow/tools_types.go:683—MCPServerConfigembeds Base; adds Mode, Toolsets, GuardPolicies, CustomFieldsRecommendation: The base already exists — good. The divergence between
parserandworkflowderivatives suggests a missing intermediate type or a clearer ownership boundary. Document which layer owns which fields, or pushMode/Toolsets/GuardPoliciesinto the base if they apply registry-side.Cluster 2: Sandbox / SRT Config Quintet
Severity: Medium — deeply nested hierarchy.
Locations in
pkg/workflow/sandbox.go::33SandboxConfig:44AgentSandboxConfig:60SandboxRuntimeConfig:71SRTNetworkConfig:80SRTFilesystemConfigRecommendation: Five tightly-coupled types in one file. Consider whether
SandboxRuntimeConfigcould embed its network/filesystem subtypes anonymously, or whether the top three could collapse.Cluster 3: Update-Entity Configs (near-duplicates, NO shared base)
Severity: High — three independent structs with >70% field overlap.
Locations:
pkg/workflow/update_issue.go—UpdateIssuesConfigpkg/workflow/update_pull_request.go—UpdatePullRequestsConfigpkg/workflow/update_discussion.go—UpdateDiscussionsConfigRecommendation: Mirror the pattern already used by
CloseEntityConfig(seepkg/workflow/close_entity_helpers.go:203which uses one base + three aliases). This is the cheapest, highest-clarity consolidation in this report.Cluster 4: Error Types (scattered, no hierarchy)
Severity: Medium — six error types with overlapping fields (Message/Reason, Severity, Suggestion).
Locations:
pkg/workflow/workflow_errors.go:40—WorkflowValidationError(Field, Value, Reason, Suggestion, Severity, Category, Timestamp, File, Line, Column)pkg/workflow/workflow_errors.go—OperationError,ConfigurationErrorpkg/workflow/error_recovery.go:24—PrioritizedError(Message, Severity, Category, Suggestion)pkg/cli/audit_report.go:143—ErrorInfopkg/cli/logs_report_errors.go—ErrorSummaryRecommendation: Extract a
BaseErrorwith Message/Severity/Category/Suggestion. Validation-shaped errors add File/Line/Column. None of these implement a common interface beyonderror.Cluster 5: Result Types (no shared shape)
Severity: Medium-low — seven structurally similar result types.
Locations:
pkg/cli/forecast.go:72—ForecastWorkflowResultpkg/cli/forecast.go—ForecastVariantResult,ForecastResultpkg/cli/trial_types.go:6—WorkflowTrialResultpkg/cli/trial_types.go:17—CombinedTrialResultpkg/cli/run_workflow_execution.go—WorkflowRunResultpkg/cli/compile_config.go—ValidationResultRecommendation: Lower priority — these are semantically distinct enough that consolidation may not pay off. Worth a single naming-convention pass (
*Resultis consistent at least).Cluster 6: AWF Config Nesting
Severity: Low — five nested types in
pkg/workflow/awf_config.go(AWFConfigFile,AWFNetworkConfig,AWFAPIProxyConfig,AWFAPITargetConfig,AWFContainerConfig). Composition is intentional; no action recommended beyond documenting the hierarchy.Cluster 7: Cross-Package Aliases (workflow re-exports actionpins)
Severity: Low —
pkg/workflow/action_pins.go:17aliasesActionYAMLInput,ActionPin,ActionPinsData,ContainerPinfrompkg/actionpins/. Expands the workflow package's public surface unnecessarily. Inline-use the original types or move the canonical definitions to a shared location.Cluster 8: Frontmatter Sub-Configs (single-file group)
Severity: Low —
pkg/workflow/frontmatter_types.godefines ~8 nested configs (FrontmatterConfig,RuntimeConfig,PermissionsConfig,ExperimentConfig,RateLimitConfig,OTLPConfig,ObservabilityConfig,GitHubActionsPermissionsConfig,GitHubAppPermissionsConfig). The composition is intentional; no action recommended.Documented Alias Clusters — SKIP (intentional)
These are flagged as duplicates by structural analysis but are deliberate type aliases with comments explaining the design:
pkg/workflow/missing_issue_reporting.go:26-42—MissingDataConfig,MissingToolConfig,ReportIncompleteConfigare all= IssueReportingConfig. The comment at line 24 explicitly justifies this for semantic clarity at usage sites.pkg/workflow/close_entity_helpers.go:202-205—CloseIssuesConfig,ClosePullRequestsConfig,CloseDiscussionsConfigare all= CloseEntityConfig. Comment labels these as backward-compatibility aliases.Do not consolidate these — the alias-with-shared-base pattern is the right one and is already in use.
Untyped Usages
Summary Statistics
interface{}literal usages: 0 (already migrated)anyparameter: 588map[string]anyusages: 1,815map[string]any: 315Category 1: YAML/Frontmatter Parsing Threads
anyThrough Many Layers (HIGH)This is the largest single source of untyped code in the repo. YAML is unmarshalled into
map[string]any, then walked with type assertions through extraction helpers.Examples:
pkg/workflow/tools_parser.go:187+—parseGitHubTool(val any),parseBashTool(val any),parseWebFetchTool(val any), ~15 sibling parsers. All immediately assertval.(map[string]any).pkg/workflow/mcp_github_config.go:84—hasGitHubApp(githubTool any) bool— defensive cast, returns default on failure.pkg/workflow/mcp_github_config.go:138—getGitHubType(githubTool any).pkg/workflow/mcp_github_config.go:163—getGitHubToken(githubTool any).pkg/workflow/frontmatter_extraction_security.go:146—extractAgentSandboxConfig(agentVal any) *AgentSandboxConfig.pkg/workflow/frontmatter_extraction_security.go:252—extractMCPGatewayConfig(mcpVal any) *MCPGatewayRuntimeConfig.pkg/workflow/frontmatter_extraction_security.go:448—extractSRTConfig(configVal any) *SandboxRuntimeConfig.Recommendation: Define intermediate raw-input structs (e.g.
type rawGitHubTool struct{ Mode *string "yaml:\"mode\""; ... }) and unmarshal YAML directly into them. Today the codebase pays the type-assertion cost on every field access; switching to struct unmarshalling pays it once per config block.Category 2: Guard Policies Returned as
map[string]any(HIGH)pkg/workflow/mcp_github_config.go:284—getGitHubGuardPolicies(githubTool any) map[string]anypkg/workflow/mcp_github_config.go:414—deriveSafeOutputsGuardPolicyFromGitHub(githubTool any) map[string]anyCallers cannot validate structure at compile time and must know magic keys.
Recommendation: Define a
GuardPolicystruct (or set of structs by policy kind) and return that. The shape is small and stable.Category 3: Safe-Output Handler Registry Returns Untyped Maps (MEDIUM)
pkg/workflow/compiler_safe_outputs_handlers.go:7-148+registers ~8 handlers, all shaped:The returned map is then serialized to YAML. A typed return (or a generated struct per output kind) would make the YAML schema discoverable from Go.
Category 4: MCP Validation Functions Take Untyped Maps (MEDIUM)
pkg/workflow/mcp_property_validation.go:48—validateMCPRequirements(toolName string, mcpConfig map[string]any, toolConfig map[string]any) errorpkg/workflow/mcp_property_validation.go:254—buildSchemaMCPConfig(toolConfig map[string]any) map[string]anyRecommendation: After the parser populates a typed
MCPServerConfig(Cluster 1 above), validation should operate on the typed value, not raw maps.Category 5: Schema Validation Accepts
anyWorkflow Data (MEDIUM)pkg/workflow/schema_validation.go:111—(c *Compiler) validateGitHubActionsSchemaFromParsed(workflowData any) errorpkg/parser/schema_compiler.go:216—normalizeForJSONSchema(v any) anyFull-workflow input passed as
any. The schema validator does need to walk arbitrary YAML, soanyhere is partially justified — but the entry point could take a typed*WorkflowDataand convert internally.Category 6: Typeutil Convert Helpers (MEDIUM — partially justified)
pkg/typeutil/convert.go::48—ParseIntValue(value any) (int, bool):95—ConvertToInt(val any) int:133—ConvertToFloat(val any) float64:150—LookupMap(m map[string]any, key string) (map[string]any, bool)These genuinely deal with heterogeneous YAML/JSON input, so the
anysignature is justified. They are the right place to absorb the dynamic typing; the callers should not be propagatinganyfurther.Category 7: Untyped String Constants Used as Enums (MEDIUM)
Examples:
pkg/workflow/cache_integrity.go:16—const defaultCacheIntegrityLevel = "none"(untyped string)pkg/constants/—const AWFDefaultLogLevel = "info"(untyped string)Recommendation: Introduce named string types —
type LogLevel string,type CacheIntegrityLevel string— and type the constants. Compile-time checks catch typos in constant assignments and switch statements.Category 8: Dispatch Workflow Generation Returns
map[string]any(LOW-MEDIUM)pkg/workflow/safe_outputs_dispatch.go:108—generateDispatchWorkflowTool(workflowName string, workflowInputs map[string]any) map[string]anyGenerated YAML structure not encoded in types.
Skip — Idiomatic
anyUsageThese use
anylegitimately and should be left as-is:pkg/logger/slog_adapter.go:86—formatSlogValue(v any) string(log value formatting needs reflection).pkg/console/render.go:28—RenderStruct(v any) string(reflection-based rendering).pkg/console/layout_wasm.go:16—LayoutEmphasisBox(content string, color any) string(layout primitives).error,fmt.Stringer,json.Marshaler— these must be interface-typed.Refactoring Recommendations
Priority 1: Consolidate
UpdateEntityConfigfamily (HIGH impact, LOW effort)Mirror the existing
CloseEntityConfig+ three aliases pattern (pkg/workflow/close_entity_helpers.go:202-205).Steps:
UpdateEntityConfiginpkg/workflow/update_entity_helpers.go(new file) collecting the shared fields from the three existing types.update_issue.go,update_pull_request.go,update_discussion.gowithtype Update*Config = UpdateEntityConfigaliases.EntityType.go test ./....Estimated effort: 2–4 hours. Benefit: removes a near-duplicate cluster and mirrors a pattern that's already validated in the codebase.
Priority 2: Type the YAML frontmatter extraction surface (HIGH impact, MEDIUM-HIGH effort)
Replace
extract*(val any)helpers inpkg/workflow/frontmatter_extraction_*.goandpkg/workflow/tools_parser.gowith struct unmarshalling.Steps:
extractXxx(val any) *XxxConfig, define a privaterawXxxstruct withyamltags matching the keys currently accessed via map lookup.val.(map[string]any)+ key-by-key extraction withyaml.Unmarshal(or a directyaml.Nodedecode) intorawXxx, then a small converter fromrawXxx→*XxxConfig.pkg/typeutil/convert.goas the absorption layer for cases where the YAML key can hold multiple types (string-or-bool, etc.) — these are legitimately dynamic.go test ./...per file converted.This is the single highest-impact change available — it touches the largest cluster of
anyusage and the bulk of the codebase's defensive type assertions.Estimated effort: 1–2 days, ideally as several PRs split by extraction module (security, metadata, tools).
Priority 3: Define
GuardPolicytypes (MEDIUM impact, LOW effort)Replace
map[string]anyreturns inpkg/workflow/mcp_github_config.go:284and:414with a typedGuardPolicystruct. The shape is small and stable based on current callers.Estimated effort: 2–3 hours.
Priority 4: Named string types for enum-like constants (MEDIUM impact, LOW effort)
Introduce
type LogLevel string,type CacheIntegrityLevel string, etc., and type the corresponding constants. Pure additive change — no callers need to update if the named type is a string alias used in declarations only.Estimated effort: 1–2 hours.
Priority 5: Error-type base struct (LOW impact, LOW-MEDIUM effort)
Introduce
BaseError(Message, Severity, Category, Suggestion) embedded inWorkflowValidationError,PrioritizedError,OperationError,ConfigurationError,ErrorInfo,ErrorSummary. Keep validation-specific fields (File/Line/Column) on the validation subtype.Estimated effort: 3–4 hours.
What NOT to Refactor
IssueReportingConfig/CloseEntityConfigalias families — already optimal, documented as intentional.pkg/typeutil/convert.goanysignatures — correct location for dynamic-type absorption.pkg/logger/slog_adapter.go,pkg/console/render.go—anyis idiomatic for log/render primitives.errorinterface satisfiers everywhere —anyhere is not a code smell.Implementation Checklist
Update*Configfamily usingCloseEntityConfigpatternpkg/workflow/frontmatter_extraction_security.go)pkg/workflow/frontmatter_extraction_metadata.go)pkg/workflow/tools_parser.go)map[string]anyreturns withGuardPolicystructLogLevel,CacheIntegrityLevel, and similar string enumsBaseErrorembedded typego test ./...after each consolidationAnalysis Metadata
interface{}literal usages: 0 (codebase has migrated toany)anyparameter: 588map[string]anyusages: 1,815 across 315 filesBeta Was this translation helpful? Give feedback.
All reactions