diff --git a/DEADCODE.md b/DEADCODE.md index 07bd662d83f..29afbcea069 100644 --- a/DEADCODE.md +++ b/DEADCODE.md @@ -44,133 +44,418 @@ make fmt --- -## Batch plan (248 dead functions as of 2026-02-28) +## Batch plan (107 dead functions as of 2026-03-02) -Each batch: delete the dead functions, delete the tests that exclusively test them, run verification, commit, open PR. +Batches 1–4 have been completed. The original batches 5–16 are superseded by this plan; +many of those functions were removed during prior work, and the remainder (plus newly +discovered dead code) are redistributed below into 30 focused phases. -### Batch 5 — simple helpers (11 functions) -Files: `pkg/workflow/validation_helpers.go` (6), `pkg/workflow/map_helpers.go` (5) +Each phase: delete the dead functions, delete tests that exclusively test them, +run verification, commit, open PR. -Dead functions: -- `ValidateRequired`, `ValidateMaxLength`, `ValidateMinLength`, `ValidateInList`, `ValidatePositiveInt`, `ValidateNonNegativeInt` -- `isEmptyOrNil`, `getMapFieldAsString`, `getMapFieldAsMap`, `getMapFieldAsBool`, `getMapFieldAsInt` +**WASM false positives (do not delete):** +- `Compiler.CompileToYAML` (`compiler_string_api.go:15`) — used by `cmd/gh-aw-wasm` +- `Compiler.ParseWorkflowString` (`compiler_string_api.go:52`) — used by `cmd/gh-aw-wasm` -Tests to remove from `validation_helpers_test.go`: -- `TestValidateRequired`, `TestValidateMaxLength`, `TestValidateMinLength`, `TestValidateInList`, `TestValidatePositiveInt`, `TestValidateNonNegativeInt` -- `TestIsEmptyOrNil`, `TestGetMapFieldAsString`, `TestGetMapFieldAsMap`, `TestGetMapFieldAsBool`, `TestGetMapFieldAsInt` +**Shared test infrastructure (do not delete):** +- `containsInNonCommentLines`, `indexInNonCommentLines`, `extractJobSection` (`compiler_test_helpers.go`) — used by ≥15 test files -### Batch 6 — engine helpers (5 functions) -File: `pkg/workflow/engine_helpers.go` (5) +--- + +### Phase 5 — CLI git helpers (3 functions) +File: `pkg/cli/git.go` + +| Function | Line | +|----------|------| +| `getDefaultBranch` | 496 | +| `checkOnDefaultBranch` | 535 | +| `confirmPushOperation` | 569 | + +Tests to check: `git_test.go` — remove `TestGetDefaultBranch`, `TestCheckOnDefaultBranch`, `TestConfirmPushOperation` if they exist. + +### Phase 6 — parser frontmatter parsing & hashing (5 functions) +Files: `pkg/parser/frontmatter_content.go` (2), `pkg/parser/frontmatter_hash.go` (3) + +| Function | File | Line | +|----------|------|------| +| `ExtractFrontmatterString` | `frontmatter_content.go` | 141 | +| `ExtractYamlChunk` | `frontmatter_content.go` | 181 | +| `ComputeFrontmatterHash` | `frontmatter_hash.go` | 50 | +| `buildCanonicalFrontmatter` | `frontmatter_hash.go` | 80 | +| `ComputeFrontmatterHashWithExpressions` | `frontmatter_hash.go` | 346 | + +Tests to check: `frontmatter_content_test.go`, `frontmatter_hash_test.go`. + +### Phase 7 — parser URL & schema helpers (4 functions) +Files: `pkg/parser/github_urls.go` (3), `pkg/parser/schema_compiler.go` (1) + +| Function | File | Line | +|----------|------|------| +| `ParseRunURL` | `github_urls.go` | 316 | +| `GitHubURLComponents.GetRepoSlug` | `github_urls.go` | 422 | +| `GitHubURLComponents.GetWorkflowName` | `github_urls.go` | 427 | +| `GetMainWorkflowSchema` | `schema_compiler.go` | 382 | + +Tests to check: `github_urls_test.go`, `schema_compiler_test.go`. + +### Phase 8 — parser import system (4 functions) +Files: `pkg/parser/import_error.go` (2), `pkg/parser/import_processor.go` (2) + +| Function | File | Line | +|----------|------|------| +| `ImportError.Error` | `import_error.go` | 30 | +| `ImportError.Unwrap` | `import_error.go` | 35 | +| `ProcessImportsFromFrontmatter` | `import_processor.go` | 78 | +| `ProcessImportsFromFrontmatterWithManifest` | `import_processor.go` | 90 | + +Note: If `ImportError` struct has no remaining methods, consider deleting the type entirely. + +Tests to check: `import_error_test.go`, `import_processor_test.go`. + +### Phase 9 — compiler option functions part 1 (5 functions) +File: `pkg/workflow/compiler_types.go` + +| Function | Line | +|----------|------| +| `WithCustomOutput` | 26 | +| `WithVersion` | 31 | +| `WithSkipValidation` | 36 | +| `WithNoEmit` | 41 | +| `WithStrictMode` | 46 | + +Tests to check: `compiler_types_test.go`, `compiler_test.go` — remove tests for these `With*` option constructors. + +### Phase 10 — compiler option functions part 2 (5 functions) +File: `pkg/workflow/compiler_types.go` + +| Function | Line | +|----------|------| +| `WithForceRefreshActionPins` | 56 | +| `WithWorkflowIdentifier` | 61 | +| `NewCompilerWithVersion` | 160 | +| `Compiler.GetSharedActionResolverForTest` | 305 | +| `Compiler.GetArtifactManager` | 333 | + +Note: `GetSharedActionResolverForTest` may be used only in tests — delete it AND any test callers. +After this phase, clean up the `CompilerOption` type if no live `With*` functions remain. + +### Phase 11 — agentic engine (3 functions) +File: `pkg/workflow/agentic_engine.go` + +| Function | Line | +|----------|------| +| `BaseEngine.convertStepToYAML` | 333 | +| `GenerateSecretValidationStep` | 430 | +| `EngineRegistry.GetAllEngines` | 502 | + +Tests to check: `agentic_engine_test.go`. + +### Phase 12 — error handling utilities (5 functions) +Files: `pkg/workflow/error_aggregation.go` (3), `pkg/workflow/error_helpers.go` (2) + +| Function | File | Line | +|----------|------|------| +| `ErrorCollector.HasErrors` | `error_aggregation.go` | 92 | +| `FormatAggregatedError` | `error_aggregation.go` | 144 | +| `SplitJoinedErrors` | `error_aggregation.go` | 174 | +| `EnhanceError` | `error_helpers.go` | 165 | +| `WrapErrorWithContext` | `error_helpers.go` | 187 | + +Tests to check: `error_aggregation_test.go`, `error_helpers_test.go`. + +### Phase 13 — safe outputs env vars (4 functions) +File: `pkg/workflow/safe_outputs_env.go` + +| Function | Line | +|----------|------| +| `applySafeOutputEnvToSlice` | 47 | +| `buildTitlePrefixEnvVar` | 311 | +| `buildLabelsEnvVar` | 321 | +| `buildCategoryEnvVar` | 332 | + +Tests to check: `safe_outputs_env_test.go`, `safe_output_helpers_test.go`. + +### Phase 14 — safe outputs config helpers (3 functions) +File: `pkg/workflow/safe_outputs_config_helpers.go` + +| Function | Line | +|----------|------| +| `getEnabledSafeOutputToolNamesReflection` | 85 | +| `Compiler.formatDetectionRunsOn` | 127 | +| `GetEnabledSafeOutputToolNames` | 216 | + +Tests to check: `safe_outputs_config_helpers_test.go`, `threat_detection_test.go`. -Dead functions: `ExtractAgentIdentifier`, `GetHostedToolcachePathSetup`, `GetSanitizedPATHExport`, `GetToolBinsSetup`, `GetToolBinsEnvArg` +### Phase 15 — Playwright MCP config (3 functions) +File: `pkg/workflow/mcp_playwright_config.go` -Tests to remove from `engine_helpers_test.go`: -- `TestExtractAgentIdentifier`, `TestGetHostedToolcachePathSetup`, `TestGetHostedToolcachePathSetup_Consistency`, `TestGetHostedToolcachePathSetup_UsesToolBins`, `TestGetToolBinsSetup`, `TestGetToolBinsEnvArg`, `TestGetSanitizedPATHExport`, `TestGetSanitizedPATHExport_ShellExecution` +| Function | Line | +|----------|------| +| `getPlaywrightDockerImageVersion` | 15 | +| `getPlaywrightMCPPackageVersion` | 26 | +| `generatePlaywrightDockerArgs` | 32 | -### Batch 7 — domain helpers (10 functions) -File: `pkg/workflow/domains.go` (10) +Tests to check: `mcp_playwright_config_test.go`. -Dead functions: `mergeDomainsWithNetwork`, `mergeDomainsWithNetworkAndTools`, `GetCopilotAllowedDomains`, `GetCopilotAllowedDomainsWithSafeInputs`, `GetCopilotAllowedDomainsWithTools`, `GetCodexAllowedDomains`, `GetCodexAllowedDomainsWithTools`, `GetClaudeAllowedDomains`, `GetClaudeAllowedDomainsWithSafeInputs`, `GetClaudeAllowedDomainsWithTools` +### Phase 16 — MCP config builtins (3 functions) +File: `pkg/workflow/mcp_config_builtin.go` -Tests to remove from `domains_test.go`, `domains_protocol_test.go`, `domains_sort_test.go`, `safe_inputs_firewall_test.go`, `http_mcp_domains_test.go` — remove only the specific test functions that call these dead helpers; keep tests for live functions in those files. +| Function | Line | +|----------|------| +| `renderSafeOutputsMCPConfig` | 113 | +| `renderSafeOutputsMCPConfigTOML` | 295 | +| `renderAgenticWorkflowsMCPConfigTOML` | 308 | -### Batch 8 — expression graph (16 functions) -Files: `pkg/workflow/expression_nodes.go` (4), `pkg/workflow/expression_builder.go` (9), `pkg/workflow/known_needs_expressions.go` (3) +Tests to check: `mcp_config_builtin_test.go`, `mcp_config_refactor_test.go`, `mcp_config_shared_test.go`. -Dead functions in `expression_nodes.go`: `ParenthesesNode.Render`, `NumberLiteralNode.Render`, `TernaryNode.Render`, `ContainsNode.Render` +### Phase 17 — MCP config miscellaneous (4 functions) +Files: `pkg/workflow/mcp_config_custom.go` (1), `mcp_config_playwright_renderer.go` (1), `mcp_config_types.go` (1), `mcp_config_validation.go` (1) -Dead functions in `expression_builder.go`: `BuildNumberLiteral`, `BuildContains`, `BuildTernary`, `BuildLabelContains`, `BuildActionEquals`, `BuildRefStartsWith`, `BuildExpressionWithDescription`, `BuildPRCommentCondition`, `AddDetectionSuccessCheck` +| Function | File | Line | +|----------|------|------| +| `renderCustomMCPConfigWrapper` | `mcp_config_custom.go` | 21 | +| `renderPlaywrightMCPConfig` | `mcp_config_playwright_renderer.go` | 71 | +| `MapToolConfig.GetAny` | `mcp_config_types.go` | 99 | +| `getTypeString` | `mcp_config_validation.go` | 176 | -Dead functions in `known_needs_expressions.go`: `getSafeOutputJobNames`, `hasMultipleSafeOutputTypes`, `getCustomJobNames` +Tests to check: `mcp_config_custom_test.go`, `mcp_config_playwright_renderer_test.go`, `mcp_config_types_test.go`, `mcp_config_validation_test.go`. -Tests to find and remove: check `expressions_test.go`, `expression_coverage_test.go`, `known_needs_expressions_test.go`. +### Phase 18 — safe inputs system (4 functions) +Files: `pkg/workflow/safe_inputs_generator.go` (1), `safe_inputs_parser.go` (2), `safe_inputs_renderer.go` (1) -### Batch 9 — constants & console (18 functions) -Files: `pkg/constants/constants.go` (13), `pkg/console/console.go` (5) +| Function | File | Line | +|----------|------|------| +| `GenerateSafeInputGoToolScriptForInspector` | `safe_inputs_generator.go` | 391 | +| `IsSafeInputsHTTPMode` | `safe_inputs_parser.go` | 64 | +| `ParseSafeInputs` | `safe_inputs_parser.go` | 210 | +| `getSafeInputsEnvVars` | `safe_inputs_renderer.go` | 14 | -Dead functions in `constants.go`: all `String()`/`IsValid()` methods on `LineLength`, `FeatureFlag`, `URL`, `ModelName`, `WorkflowID`, `EngineName`, plus `MCPServerID.IsValid` +Tests to check: `safe_inputs_generator_test.go`, `safe_inputs_parser_test.go`, `safe_inputs_renderer_test.go`. -Dead functions in `console.go`: `FormatLocationMessage`, `FormatCountMessage`, `FormatListHeader`, `RenderTree`, `buildLipglossTree` +### Phase 19 — safe outputs validation & safe jobs (3 functions) +Files: `pkg/workflow/safe_output_validation_config.go` (2), `safe_jobs.go` (1) -Tests to remove: relevant subtests in `constants_test.go`; `TestFormatLocationMessage`, `TestRenderTree`, `TestRenderTreeSimple`, `TestFormatCountMessage`, `TestFormatListHeader` in `console_test.go` and related files. +| Function | File | Line | +|----------|------|------| +| `GetValidationConfigForType` | `safe_output_validation_config.go` | 409 | +| `GetDefaultMaxForType` | `safe_output_validation_config.go` | 415 | +| `HasSafeJobsEnabled` | `safe_jobs.go` | 34 | -### Batch 10 — agent session builder (1 function) -File: `pkg/workflow/create_agent_session.go` +Tests to check: `safe_output_validation_config_test.go`, `safe_jobs_test.go`. -Dead function: `Compiler.buildCreateOutputAgentSessionJob` +### Phase 20 — safe output job builders: comments & discussions (4 functions) +Files: `pkg/workflow/add_comment.go` (1), `create_code_scanning_alert.go` (1), `create_discussion.go` (1), `create_pr_review_comment.go` (1) -Find and remove its test(s): `grep -rn "buildCreateOutputAgentSessionJob" --include="*_test.go"`. +| Function | File | Line | +|----------|------|------| +| `Compiler.buildCreateOutputAddCommentJob` | `add_comment.go` | 34 | +| `Compiler.buildCreateOutputCodeScanningAlertJob` | `create_code_scanning_alert.go` | 21 | +| `Compiler.buildCreateOutputDiscussionJob` | `create_discussion.go` | 132 | +| `Compiler.buildCreateOutputPullRequestReviewCommentJob` | `create_pr_review_comment.go` | 24 | -### Batch 11 — safe-outputs & MCP helpers (13 functions) -Files: `pkg/workflow/safe_outputs_env.go` (4), `pkg/workflow/safe_outputs_config_helpers.go` (3), `pkg/workflow/mcp_playwright_config.go` (3), `pkg/workflow/mcp_config_builtin.go` (3) +Note: If these are the only functions in their files, consider deleting the entire file. -Dead functions in `safe_outputs_env.go`: `applySafeOutputEnvToSlice`, `buildTitlePrefixEnvVar`, `buildLabelsEnvVar`, `buildCategoryEnvVar` +Tests to check: `add_comment_test.go`, `create_code_scanning_alert_test.go`, `create_discussion_test.go`, `create_pr_review_comment_test.go`. -Dead functions in `safe_outputs_config_helpers.go`: `getEnabledSafeOutputToolNamesReflection`, `Compiler.formatDetectionRunsOn`, `GetEnabledSafeOutputToolNames` +### Phase 21 — safe output job builders: sessions & missing data (3 functions) +Files: `pkg/workflow/create_agent_session.go` (1), `missing_data.go` (1), `missing_tool.go` (1) -Dead functions in `mcp_playwright_config.go`: `getPlaywrightDockerImageVersion`, `getPlaywrightMCPPackageVersion`, `generatePlaywrightDockerArgs` +| Function | File | Line | +|----------|------|------| +| `Compiler.buildCreateOutputAgentSessionJob` | `create_agent_session.go` | 88 | +| `Compiler.buildCreateOutputMissingDataJob` | `missing_data.go` | 12 | +| `Compiler.buildCreateOutputMissingToolJob` | `missing_tool.go` | 12 | -Dead functions in `mcp_config_builtin.go`: `renderSafeOutputsMCPConfig`, `renderSafeOutputsMCPConfigTOML`, `renderAgenticWorkflowsMCPConfigTOML` +Note: If the file contains only the dead function, delete the entire file. -Tests to remove: check `safe_output_helpers_test.go`, `version_field_test.go`, `mcp_benchmark_test.go`, `mcp_config_refactor_test.go`, `mcp_config_shared_test.go`, `threat_detection_test.go`. +Tests to check: `create_agent_session_test.go`, `missing_data_test.go`, `missing_tool_test.go`. -### Batch 12 — small utilities (9 functions) -Files: `pkg/sliceutil/sliceutil.go` (3), `pkg/stringutil/pat_validation.go` (3), `pkg/workflow/error_aggregation.go` (3) +### Phase 22 — safe output compilation (3 functions) +Files: `pkg/workflow/compiler_safe_outputs.go` (2), `compiler_safe_outputs_specialized.go` (1) -Dead functions in `sliceutil.go`: `ContainsAny`, `ContainsIgnoreCase`, `FilterMap` +| Function | File | Line | +|----------|------|------| +| `Compiler.generateJobName` | `compiler_safe_outputs.go` | 185 | +| `Compiler.mergeSafeJobsFromIncludes` | `compiler_safe_outputs.go` | 219 | +| `Compiler.buildCreateProjectStepConfig` | `compiler_safe_outputs_specialized.go` | 139 | -Dead functions in `pat_validation.go`: `IsFineGrainedPAT`, `IsClassicPAT`, `IsOAuthToken` +Tests to check: `compiler_safe_outputs_test.go`, `compiler_safe_outputs_specialized_test.go`. -Dead functions in `error_aggregation.go`: `ErrorCollector.HasErrors`, `FormatAggregatedError`, `SplitJoinedErrors` +### Phase 23 — issue reporting (2 functions) +File: `pkg/workflow/missing_issue_reporting.go` -### Batch 13 — parser utilities (9 functions) -Files: `pkg/parser/include_expander.go` (3), `pkg/parser/schema_validation.go` (3), `pkg/parser/yaml_error.go` (3) +| Function | Line | +|----------|------| +| `Compiler.buildIssueReportingJob` | 48 | +| `envVarPrefix` | 175 | -Dead functions in `include_expander.go`: `ExpandIncludes`, `ProcessIncludesForEngines`, `ProcessIncludesForSafeOutputs` +Note: If these are the only non-trivial functions in the file, consider deleting it entirely. -Dead functions in `schema_validation.go`: `ValidateMainWorkflowFrontmatterWithSchema`, `ValidateIncludedFileFrontmatterWithSchema`, `ValidateMCPConfigWithSchema` +Tests to check: `missing_issue_reporting_test.go`. -Dead functions in `yaml_error.go`: `ExtractYAMLError`, `extractFromGoccyFormat`, `extractFromStringParsing` +### Phase 24 — checkout manager (2 functions) +File: `pkg/workflow/checkout_manager.go` -### Batch 14 — agentic engine & compiler types (16 functions) -Files: `pkg/workflow/agentic_engine.go` (3), `pkg/workflow/compiler_types.go` (10+), `pkg/cli/docker_images.go` (6) +| Function | Line | +|----------|------| +| `CheckoutManager.GetCurrentRepository` | 186 | +| `getCurrentCheckoutRepository` | 553 | -Dead functions in `agentic_engine.go`: `BaseEngine.convertStepToYAML`, `GenerateSecretValidationStep`, `EngineRegistry.GetAllEngines` +Tests to check: `checkout_manager_test.go`. -Dead functions in `compiler_types.go` (check WASM binary first): `WithCustomOutput`, `WithVersion`, `WithSkipValidation`, `WithNoEmit`, `WithStrictMode`, `WithForceRefreshActionPins`, `WithWorkflowIdentifier`, `NewCompilerWithVersion`, `Compiler.GetSharedActionResolverForTest`, `Compiler.GetArtifactManager` +### Phase 25 — expression processing (3 functions) +Files: `pkg/workflow/expression_extraction.go` (1), `expression_parser.go` (1), `expression_validation.go` (1) -Dead functions in `docker_images.go`: `isDockerAvailable`, `ResetDockerPullState`, `ValidateMCPServerDockerAvailability`, `SetDockerImageDownloading`, `SetMockImageAvailable`, `PrintDockerPullStatus` +| Function | File | Line | +|----------|------|------| +| `ExpressionExtractor.GetMappings` | `expression_extraction.go` | 239 | +| `NormalizeExpressionForComparison` | `expression_parser.go` | 463 | +| `ValidateExpressionSafetyPublic` | `expression_validation.go` | 359 | -### Batch 15 — js.go stubs (6 functions) -File: `pkg/workflow/js.go` +Tests to check: `expression_extraction_test.go`, `expression_parser_test.go`, `expression_validation_test.go`. + +### Phase 26 — frontmatter extraction (3 functions) +Files: `pkg/workflow/frontmatter_extraction_metadata.go` (1), `frontmatter_extraction_yaml.go` (1), `frontmatter_types.go` (1) + +| Function | File | Line | +|----------|------|------| +| `extractMapFromFrontmatter` | `frontmatter_extraction_metadata.go` | 246 | +| `Compiler.extractYAMLValue` | `frontmatter_extraction_yaml.go` | 18 | +| `unmarshalFromMap` | `frontmatter_types.go` | 196 | + +Tests to check: `frontmatter_extraction_metadata_test.go`, `frontmatter_extraction_yaml_test.go`, `frontmatter_types_test.go`. + +### Phase 27 — git, GitHub CLI & shell helpers (4 functions) +Files: `pkg/workflow/git_helpers.go` (2), `github_cli.go` (1), `shell.go` (1) + +| Function | File | Line | +|----------|------|------| +| `GetCurrentGitTag` | `git_helpers.go` | 69 | +| `RunGit` | `git_helpers.go` | 119 | +| `ExecGHWithOutput` | `github_cli.go` | 84 | +| `shellEscapeCommandString` | `shell.go` | 82 | + +Tests to check: `git_helpers_test.go`, `github_cli_test.go`, `shell_test.go`. + +### Phase 28 — config & concurrency validation (4 functions) +Files: `pkg/workflow/config_helpers.go` (2), `concurrency_validation.go` (1), `permissions_validation.go` (1) + +| Function | File | Line | +|----------|------|------| +| `parseParticipantsFromConfig` | `config_helpers.go` | 131 | +| `ParseIntFromConfig` | `config_helpers.go` | 218 | +| `extractGroupExpression` | `concurrency_validation.go` | 289 | +| `GetToolsetsData` | `permissions_validation.go` | 77 | + +Tests to check: `config_helpers_test.go`, `concurrency_validation_test.go`, `permissions_validation_test.go`. + +### Phase 29 — security & error types (4 functions) +Files: `pkg/workflow/markdown_security_scanner.go` (1), `secrets_validation.go` (1), `tools_validation.go` (1), `shared_workflow_error.go` (1) + +| Function | File | Line | +|----------|------|------| +| `SecurityFinding.String` | `markdown_security_scanner.go` | 64 | +| `validateSecretReferences` | `secrets_validation.go` | 31 | +| `isGitToolAllowed` | `tools_validation.go` | 31 | +| `NewSharedWorkflowError` | `shared_workflow_error.go` | 21 | + +Note: If `SharedWorkflowError` has no remaining constructor, consider deleting the type entirely. + +Tests to check: `markdown_security_scanner_test.go`, `secrets_validation_test.go`, `tools_validation_test.go`, `shared_workflow_error_test.go`. + +### Phase 30 — step & job types (3 functions) +Files: `pkg/workflow/step_types.go` (2), `jobs.go` (1) + +| Function | File | Line | +|----------|------|------| +| `WorkflowStep.IsRunStep` | `step_types.go` | 36 | +| `WorkflowStep.ToYAML` | `step_types.go` | 171 | +| `JobManager.GetTopologicalOrder` | `jobs.go` | 412 | + +Tests to check: `step_types_test.go`, `jobs_test.go`. + +### Phase 31 — utilities cleanup (4 functions) +Files: `pkg/sliceutil/sliceutil.go` (1), `pkg/workflow/semver.go` (1), `repository_features_validation.go` (1), `compiler_yaml_ai_execution.go` (1) + +| Function | File | Line | +|----------|------|------| +| `FilterMap` | `pkg/sliceutil/sliceutil.go` | 49 | +| `extractMajorVersion` | `semver.go` | 41 | +| `ClearRepositoryFeaturesCache` | `repository_features_validation.go` | 83 | +| `Compiler.convertGoPatternToJavaScript` | `compiler_yaml_ai_execution.go` | 116 | + +Tests to check: `sliceutil_test.go`, `semver_test.go`, `repository_features_validation_test.go`, `compiler_yaml_ai_execution_test.go`. + +### Phase 32 — compiler helpers (3 functions) +Files: `pkg/workflow/compiler_yaml_helpers.go` (1), `unified_prompt_step.go` (1), `repo_memory.go` (1) + +| Function | File | Line | +|----------|------|------| +| `Compiler.generateCheckoutGitHubFolder` | `compiler_yaml_helpers.go` | 221 | +| `Compiler.generateUnifiedPromptStep` | `unified_prompt_step.go` | 30 | +| `generateRepoMemoryPushSteps` | `repo_memory.go` | 520 | + +Note: If `unified_prompt_step.go` contains only the dead function, delete the entire file. + +Tests to check: `compiler_yaml_helpers_test.go`, `unified_prompt_step_test.go`, `repo_memory_test.go`. + +### Phase 33 — metrics extraction (2 functions) +File: `pkg/workflow/metrics.go` + +| Function | Line | +|----------|------| +| `ExtractFirstMatch` | 39 | +| `ExtractMCPServer` | 274 | + +Tests to check: `metrics_test.go`. + +### Phase 34 — WASM string API audit (0 deletions) +File: `pkg/workflow/compiler_string_api.go` + +Functions: `Compiler.CompileToYAML` (line 15), `Compiler.ParseWorkflowString` (line 52) + +**Action:** Do not delete. Verify that `cmd/gh-aw-wasm/main.go` still calls these functions. +If WASM binary is ever removed, both functions become deletable. + +Run: `grep -rn "CompileToYAML\|ParseWorkflowString" cmd/gh-aw-wasm/` + +--- -Dead functions: the remaining 6 unreachable `get*Script()` / public `Get*` stubs reported by deadcode. +## Summary -### Batch 16 — artifact manager (14 functions) -File: `pkg/workflow/artifact_manager.go` +| Metric | Value | +|--------|-------| +| Total dead functions reported | 107 | +| WASM false positives (skip) | 2 | +| Shared test infrastructure (skip) | 3 | +| Functions to delete | **102** | +| Phases with deletions | 29 | +| Audit-only phases | 1 | +| Average functions per phase | 3.4 | -Save for last — most complex, with deep coupling to `artifact_manager_integration_test.go`. +**Estimated effort per phase:** 15–30 minutes (delete, test, verify, commit). +**Estimated total effort:** ~10–15 hours across all 30 phases. -### Remaining (~120 functions) -~80+ files each with 1–3 dead functions. Tackle after the above batches clear the larger clusters. +**Recommended execution order:** Phases are designed to be executed top-to-bottom. Phases within the same domain (e.g., MCP phases 15–17, safe output phases 13–14, 19–22) can be combined into larger PRs if velocity is high. --- -## Per-batch checklist +## Per-phase checklist -For each batch: +For each phase: - [ ] Run `deadcode ./cmd/... ./internal/tools/... 2>/dev/null` to confirm current dead list - [ ] For each dead function, `grep -rn "FuncName" --include="*.go"` to find all callers - [ ] Delete the function - [ ] Delete test functions that exclusively call the deleted function (not shared helpers) - [ ] Check for now-unused imports in edited files -- [ ] If editing `pkg/console/`, check `pkg/console/console_wasm.go` for calls to the deleted functions +- [ ] If deleting the last function in a file, delete the entire file +- [ ] If editing `pkg/console/`, check `pkg/console/console_wasm.go` for calls to deleted functions - [ ] `go build ./...` - [ ] `GOARCH=wasm GOOS=js go build ./pkg/console/...` (if `pkg/console/` was touched) - [ ] `go vet ./...` - [ ] `go vet -tags=integration ./...` - [ ] `make fmt` - [ ] Run selective tests for touched packages: `go test -v -run "TestAffected" ./pkg/...` -- [ ] Commit with message: `chore: remove dead functions (batch N) — X -> Y dead` +- [ ] Commit with message: `chore: remove dead functions (phase N) — X -> Y dead` - [ ] Open PR, confirm CI passes before merging diff --git a/pkg/cli/git.go b/pkg/cli/git.go index 8ec19ecac55..df4c87c99de 100644 --- a/pkg/cli/git.go +++ b/pkg/cli/git.go @@ -8,11 +8,9 @@ import ( "path/filepath" "strings" - "github.com/charmbracelet/huh" "github.com/github/gh-aw/pkg/console" "github.com/github/gh-aw/pkg/fileutil" "github.com/github/gh-aw/pkg/logger" - "github.com/github/gh-aw/pkg/workflow" ) var gitLog = logger.New("cli:git") @@ -491,121 +489,3 @@ func stageAllChanges(verbose bool) error { gitLog.Print("Successfully staged all changes") return nil } - -// getDefaultBranch gets the default branch name for the repository -func getDefaultBranch() (string, error) { - gitLog.Print("Getting default branch name") - - // Get repository slug (owner/repo) - repoSlug := getRepositorySlugFromRemote() - if repoSlug == "" { - gitLog.Print("No remote repository configured, cannot determine default branch") - return "", errors.New("no remote repository configured") - } - - // Parse owner and repo from slug - parts := strings.Split(repoSlug, "/") - if len(parts) != 2 { - gitLog.Printf("Invalid repository slug format: %s", repoSlug) - return "", fmt.Errorf("invalid repository slug format: %s", repoSlug) - } - - owner, repo := parts[0], parts[1] - - // Use ExecGH helper which handles token configuration (GH_TOKEN/GITHUB_TOKEN) - cmd := workflow.ExecGH("api", fmt.Sprintf("/repos/%s/%s", owner, repo), "--jq", ".default_branch") - output, err := cmd.Output() - if err != nil { - gitLog.Printf("Failed to get default branch: %v", err) - return "", fmt.Errorf("failed to get default branch: %w", err) - } - - defaultBranch := strings.TrimSpace(string(output)) - if defaultBranch == "" { - gitLog.Print("Empty default branch returned") - return "", errors.New("could not determine default branch") - } - - gitLog.Printf("Default branch: %s", defaultBranch) - return defaultBranch, nil -} - -// checkOnDefaultBranch checks if the current branch is the default branch -// Returns an error if no remote is configured or if not on the default branch -func checkOnDefaultBranch(verbose bool) error { - gitLog.Print("Checking if on default branch") - - // Get current branch - currentBranch, err := getCurrentBranch() - if err != nil { - return fmt.Errorf("failed to get current branch: %w", err) - } - - // Get default branch - defaultBranch, err := getDefaultBranch() - if err != nil { - // If no remote is configured, fail the push operation - if strings.Contains(err.Error(), "no remote repository configured") { - gitLog.Print("No remote configured, cannot push") - return errors.New("--push requires a remote repository to be configured") - } - return fmt.Errorf("failed to get default branch: %w", err) - } - - // Compare branches - if currentBranch != defaultBranch { - gitLog.Printf("Not on default branch: current=%s, default=%s", currentBranch, defaultBranch) - return fmt.Errorf("not on default branch: current branch is '%s', default branch is '%s'", currentBranch, defaultBranch) - } - - gitLog.Printf("On default branch: %s", currentBranch) - if verbose { - fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("✓ On default branch: "+currentBranch)) - } - return nil -} - -// confirmPushOperation prompts the user to confirm push operation (skips in CI) -func confirmPushOperation(verbose bool) error { - gitLog.Print("Checking if user confirmation is needed for push operation") - - // Skip confirmation in CI environments - if IsRunningInCI() { - gitLog.Print("Running in CI, skipping user confirmation") - if verbose { - fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Running in CI - skipping confirmation prompt")) - } - return nil - } - - // Prompt user for confirmation - gitLog.Print("Prompting user for push confirmation") - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, console.FormatWarningMessage("This will commit and push changes to the remote repository.")) - - var confirmed bool - form := huh.NewForm( - huh.NewGroup( - huh.NewConfirm(). - Title("Do you want to proceed with commit and push?"). - Description("This will stage all changes, commit them, and push to the remote repository"). - Value(&confirmed), - ), - ).WithAccessible(console.IsAccessibleMode()) - - if err := form.Run(); err != nil { - gitLog.Printf("Confirmation prompt failed: %v", err) - return fmt.Errorf("confirmation prompt failed: %w", err) - } - - if !confirmed { - gitLog.Print("User declined push operation") - return errors.New("push operation cancelled by user") - } - - gitLog.Print("User confirmed push operation") - if verbose { - fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("✓ Push operation confirmed")) - } - return nil -} diff --git a/pkg/cli/git_push_test.go b/pkg/cli/git_push_test.go deleted file mode 100644 index a1c018dbdb6..00000000000 --- a/pkg/cli/git_push_test.go +++ /dev/null @@ -1,86 +0,0 @@ -//go:build !integration - -package cli - -import ( - "os" - "os/exec" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// TestGetDefaultBranch tests the getDefaultBranch function -func TestGetDefaultBranch(t *testing.T) { - t.Run("no remote configured", func(t *testing.T) { - // Create a temporary directory for test - tmpDir := t.TempDir() - originalDir, _ := os.Getwd() - defer os.Chdir(originalDir) - - // Initialize git repository without remote - os.Chdir(tmpDir) - exec.Command("git", "init").Run() - exec.Command("git", "config", "user.email", "test@example.com").Run() - exec.Command("git", "config", "user.name", "Test User").Run() - - // Should fail because no remote is configured - _, err := getDefaultBranch() - require.Error(t, err, "Should fail when no remote is configured") - assert.Contains(t, err.Error(), "no remote repository configured") - }) -} - -// TestCheckOnDefaultBranch tests the checkOnDefaultBranch function -func TestCheckOnDefaultBranch(t *testing.T) { - t.Run("no remote configured - should fail", func(t *testing.T) { - // Create a temporary directory for test - tmpDir := t.TempDir() - originalDir, _ := os.Getwd() - defer os.Chdir(originalDir) - - // Initialize git repository without remote - os.Chdir(tmpDir) - exec.Command("git", "init").Run() - exec.Command("git", "config", "user.email", "test@example.com").Run() - exec.Command("git", "config", "user.name", "Test User").Run() - - // Create an initial commit - testFile := filepath.Join(tmpDir, "test.txt") - err := os.WriteFile(testFile, []byte("test"), 0644) - require.NoError(t, err) - exec.Command("git", "add", "test.txt").Run() - exec.Command("git", "commit", "-m", "initial commit").Run() - - // Should fail when no remote is configured - err = checkOnDefaultBranch(false) - require.Error(t, err, "Should fail when no remote is configured") - assert.Contains(t, err.Error(), "--push requires a remote repository to be configured") - }) -} - -// TestConfirmPushOperation tests the confirmPushOperation function -func TestConfirmPushOperation(t *testing.T) { - t.Run("skips confirmation in CI", func(t *testing.T) { - // Set CI environment variable - origCI := os.Getenv("CI") - os.Setenv("CI", "true") - defer func() { - if origCI == "" { - os.Unsetenv("CI") - } else { - os.Setenv("CI", origCI) - } - }() - - // Should succeed without prompting user - err := confirmPushOperation(false) - assert.NoError(t, err, "Should skip confirmation in CI") - }) - - // Note: Testing the interactive prompt outside CI is not feasible in automated tests - // as it requires user interaction. The function behavior in non-CI environments - // should be tested manually. -} diff --git a/timings.md b/timings.md new file mode 100644 index 00000000000..44d17c314b3 --- /dev/null +++ b/timings.md @@ -0,0 +1,329 @@ +# Workflow Timing Analysis + +**Workflow:** Daily Repo Status +**Run ID:** [21479574343](https://github.com/githubnext/gh-aw-trial-hono/actions/runs/21479574343) +**Date:** January 29, 2026 +**Total Duration:** 3.8 minutes (225.9 seconds) + +## Executive Summary + +The workflow consists of 6 jobs running sequentially. The **agent job dominates execution time at 64.7%** (146.2s), with actual coding agent work taking 85.4s after a 32.7s startup delay. Pre-activation and activation jobs are minimal overhead (2.2% and 2.7% respectively). + +**Critical Path:** Pre-activation → Activation → **Agent** → Detection → Safe Outputs → Conclusion + +--- + +## Complete Workflow Timeline + +``` +13:15:17 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13:19:03 + ╱pre╲──╱activation╲────────╱ agent ╲────╱detection╲╱safe╲╱conclusion╲ + 5.0s 6.2s 146.2s 21.7s 7.4s 7.4s +``` + +### Job-Level Breakdown + +| Job | Duration | % of Total | Start → End | Purpose | +|-----|----------|-----------|-------------|---------| +| **pre_activation** | 5.0s | 2.2% | 13:15:17 → 13:15:22 | Workflow validation, permission checks | +| **activation** | 6.2s | 2.7% | 13:15:29 → 13:15:35 | Environment setup, context preparation | +| **agent** | 146.2s | **64.7%** | 13:15:41 → 13:18:07 | ⚡ AI agent execution (main work) | +| **detection** | 21.7s | 9.6% | 13:18:14 → 13:18:36 | Security threat analysis | +| **safe_outputs** | 7.4s | 3.3% | 13:18:41 → 13:18:48 | Output validation and sanitization | +| **conclusion** | 7.4s | 3.3% | 13:18:56 → 13:19:03 | Workflow summary and cleanup | +| **TOTAL** | **225.9s** | **100%** | | **3 min 46s** | + +**Note:** Gaps between jobs (e.g., 7s between activation and agent) represent GitHub Actions job scheduling overhead. + +--- + +## Agent Job Deep Dive (146.2s, 64.7% of workflow) + +The agent job is the performance bottleneck. Breaking it into 5 key stages: + +### Stage-by-Stage Breakdown + +| Stage | Duration | % of Agent | Cumulative | Activities | +|-------|----------|-----------|------------|------------| +| **1. Infrastructure Setup** | 23.7s | 16.7% | 23.7s | Runner init, action downloads, checkout, git config | +| **2. Dependencies Install** | 8.8s | 6.2% | 32.5s | Claude Code CLI (npm), awf binary | +| **3. Container & Network** | 32.7s | 23.1% | 65.2s | Docker pulls, firewall setup, container start | +| **4. Start Coding Agent** | 32.7s | **23.1%** | 97.9s | ⚡ Agent initialization, MCP connections | +| **5. Agent Execution** | 85.4s | **60.2%** | 146.2s | 🤖 Actual AI coding work | + +### Detailed Agent Job Timeline + +``` +Stage 1: Infrastructure Setup (23.7s) +├─ Job Start (13:15:41) +│ └─ GitHub Actions runner initialization +├─ Setup Scripts (13:15:45) [+3.5s] +│ └─ Copy 334 JavaScript files to /opt/gh-aw/actions +├─ Checkout repository (13:15:46) [+1.1s] +├─ Configure Git credentials (13:15:47) [+0.2s] +├─ Validate secrets (13:15:47) [+0.2s] +└─ Setup Node.js (13:15:49) [+2.6s] + +Stage 2: Dependencies Installation (8.8s) +├─ Install awf binary v0.11.2 (13:15:50) [+0.8s] +└─ Install Claude Code CLI @2.1.22 (13:15:53) [+3.1s] + +Stage 3: Container & Network Setup (11.4s) +├─ Download Docker images (13:15:53) +│ ├─ github-mcp-server:v0.30.2 +│ ├─ gh-aw-mcpg:v0.0.84 +│ └─ node:lts-alpine +├─ Write safe outputs config (13:15:58) [+4.8s] +├─ Generate MCP server config (13:15:58) [+0.3s] +├─ Start safe outputs MCP server (13:15:59) [+0.1s] +└─ Start MCP gateway (13:16:04) [+5.2s] + +Stage 4: Start Coding Agent (32.7s) ⚡ KEY OPTIMIZATION TARGET +├─ Execute Claude Code CLI (13:16:05) +│ ├─ Initialize AWF firewall (13:16:06) [+0.9s] +│ ├─ Setup host-level iptables rules (13:16:06) [+0.2s] +│ ├─ Pull Docker images for agent container (13:16:06) +│ │ ├─ Pulling squid-proxy layers [8 layers] +│ │ └─ Pulling agent layers [17 layers] +│ └─ Containers started (13:16:37) [+32.0s] +└─ MCP initialization complete + +Stage 5: Agent Execution (85.4s) 🤖 ACTUAL AI WORK +├─ Claude Code processing prompt (13:16:38) +├─ Making code changes +├─ Running tools (bash, file operations, MCP calls) +└─ Stop MCP gateway (13:18:03) +``` + +--- + +## 🎯 Top 5 Optimization Opportunities + +### 1. **Pre-cache Docker Images** +**Impact:** Save 15-20s (10-14% of agent job) +**Current:** Stage 3 + Stage 4 spend 32.7s pulling containers on every run + +**Problem:** +- `ghcr.io/github/github-mcp-server:v0.30.2` (~30MB) +- `ghcr.io/githubnext/gh-aw-mcpg:v0.0.84` (~268MB) +- `node:lts-alpine` (~40MB) +- Squid-proxy and agent container images +- **Total:** Multiple container layers pulled sequentially + +**Solutions:** +- Add `docker pull` commands to workflow startup (runs once, cached) +- Use GitHub Actions runner image pre-installation +- Leverage Docker layer caching with BuildKit +- Consider using smaller base images (alpine variants) + +**Implementation:** +```yaml +- name: Pre-cache Docker images + run: | + docker pull ghcr.io/github/github-mcp-server:v0.30.2 & + docker pull ghcr.io/githubnext/gh-aw-mcpg:v0.0.84 & + docker pull node:lts-alpine & + wait +``` + +--- + +### 2. **Parallelize Setup Operations** +**Impact:** Save 8-12s (5-8% of agent job) +**Current:** Sequential execution of independent tasks + +**Parallelization Opportunities:** +- Docker image downloads (Stage 3) can run during dependency installation (Stage 2) +- MCP gateway start can overlap with Claude CLI installation +- Safe outputs config generation can run in parallel with Docker pulls + +**Implementation:** +```bash +# Run independent operations in parallel +npm install -g @anthropic-ai/claude-code@2.1.22 & +docker pull ghcr.io/github/github-mcp-server:v0.30.2 & +docker pull ghcr.io/githubnext/gh-aw-mcpg:v0.0.84 & +wait # Wait for all background jobs +``` + +--- + +### 3. **Optimize Agent Startup (Stage 4)** +**Impact:** Save 5-8s (3-6% of agent job) +**Current:** 32.7s from "Execute Agent" to "Containers Started" + +**Bottlenecks:** +- AWF firewall initialization: 0.9s +- iptables rules setup: 0.2s +- Container layer pulling: ~30s (overlaps with #1) +- Network bridge creation: ~0.5s + +**Solutions:** +- Pre-create AWF network bridges (persist across runs) +- Cache iptables configurations +- Use faster container runtime (containerd vs Docker) +- Reduce MCP connection timeouts (current: 120s, 60s for tools) + +**Configuration Change:** +```yaml +env: + MCP_TIMEOUT: 30000 # Reduce from 120s to 30s + MCP_TOOL_TIMEOUT: 20000 # Reduce from 60s to 20s +``` + +--- + +### 4. **Streamline JavaScript File Copying** +**Impact:** Save 2-3s (1-2% of agent job) +**Current:** 1.1s to copy 334 JavaScript files in Stage 1 + +**Problem:** +- Setup Scripts copies all 334 .cjs files individually +- No compression or bundling +- Redundant test files (.test.cjs) copied to production + +**Solutions:** +- Bundle JavaScript files into single archive +- Use tarball extraction instead of individual cp commands +- Exclude test files from production deployment +- Pre-install common files in runner image + +--- + +### 5. **Reduce Detection Job Time** +**Impact:** Save 8-10s (3-4% of workflow) +**Current:** 21.7s for threat detection (9.6% of total workflow) + +**Analysis Needed:** +- What does the detection job analyze? (logs, artifacts, code) +- Can it run in parallel with agent job? +- Are all detection rules necessary? + +**Potential Solutions:** +- Run detection concurrently with agent (non-blocking) +- Cache detection patterns/rules +- Use incremental analysis (only changed files) +- Reduce timeout thresholds + +--- + +## Impact Summary + +| Optimization | Time Saved | % Reduction | Effort | Priority | +|--------------|-----------|-------------|--------|----------| +| Docker image pre-caching | 15-20s | 10-14% | Medium | 🔴 High | +| Parallelize setup | 8-12s | 5-8% | Low | 🔴 High | +| Optimize agent startup | 5-8s | 3-6% | Medium | 🟡 Medium | +| JS file bundling | 2-3s | 1-2% | Low | 🟢 Low | +| Detection optimization | 8-10s | 3-4% | High | 🟡 Medium | +| **TOTAL POTENTIAL** | **38-53s** | **22-30%** | | | + +**Expected New Total:** 173-188 seconds (2.9-3.1 minutes) +**vs Current:** 225.9 seconds (3.8 minutes) + +--- + +## Quick Wins (Implement First) + +### Week 1: Immediate Improvements +1. ✅ **Pre-cache Docker images** (1-2 hour implementation) +2. ✅ **Parallelize NPM + Docker downloads** (30 min implementation) +3. ✅ **Reduce MCP timeouts** (5 min config change) + +**Expected savings:** 20-25 seconds + +### Week 2: Medium Effort +4. ⚙️ **Bundle JavaScript files** (4-6 hours) +5. ⚙️ **Optimize AWF network setup** (2-4 hours) + +**Expected savings:** 5-8 seconds + +### Future Optimization +6. 🔬 **Investigate detection parallelization** (research + design) +7. 🔬 **Custom runner image** with pre-installed tools + +--- + +## Job Dependencies & Parallelization + +Current: **All jobs run sequentially** (226s total) + +``` +pre_activation (5s) → activation (6s) → agent (146s) → detection (22s) → safe_outputs (7s) → conclusion (7s) +``` + +**Potential Parallel Execution:** + +``` +pre_activation (5s) → activation (6s) → agent (146s) ──┬→ safe_outputs (7s) → conclusion (7s) + └→ detection (22s) ──┘ +``` + +**New Critical Path:** 171s (save 22s from detection not blocking safe_outputs) + +--- + +## Time to Start Coding Agent + +**Current:** 65.2 seconds from job start to containers ready + +| Phase | Duration | Cumulative | +|-------|----------|------------| +| Infrastructure Setup | 23.7s | 23.7s | +| Dependencies Install | 8.8s | 32.5s | +| Container & Network Setup | 32.7s | 65.2s | +| **AGENT STARTS CODING** | | **65.2s** | + +**With Optimizations:** ~40-45 seconds (37% improvement) + +--- + +## Monitoring & Metrics + +### Key Performance Indicators (KPIs) + +1. **Time to Code** (TC): Job start → Agent starts coding + - Current: 65.2s + - Target: <45s + +2. **Agent Startup Time** (AST): Execute command → Containers ready + - Current: 32.7s + - Target: <20s + +3. **Total Workflow Time** (TWT): First job start → Last job end + - Current: 225.9s + - Target: <180s + +4. **Agent Efficiency** (AE): Agent execution / Total agent job time + - Current: 85.4s / 146.2s = 58.4% + - Target: >70% + +### Recommended Tracking + +Add timing annotations to workflow: +```yaml +- name: Checkpoint - Containers Ready + run: echo "::notice::Containers ready at $(date +%s)" + +- name: Checkpoint - Agent Start + run: echo "::notice::Agent execution started at $(date +%s)" +``` + +--- + +## Conclusion + +The workflow's performance bottleneck is the **agent job (146.2s, 64.7%)**, specifically: +- **Container startup (32.7s, 23%)** - Primary optimization target +- **Agent execution (85.4s, 60%)** - Actual AI work (unavoidable) + +Pre-activation (5.0s) and activation (6.2s) jobs are **already optimized** and represent minimal overhead (4.9% combined). + +**Recommended Action Plan:** +1. Implement Docker image caching immediately (biggest impact) +2. Parallelize setup operations (low effort, medium impact) +3. Reduce MCP timeouts (quick win) +4. Investigate detection job parallelization +5. Monitor KPIs and iterate + +**Expected Outcome:** Reduce total workflow time by 22-30% (from 3.8min to 2.9-3.1min) and improve "time to start coding" by 37% (from 65s to 40-45s). diff --git a/timings2.md b/timings2.md new file mode 100644 index 00000000000..59ddd2b0440 --- /dev/null +++ b/timings2.md @@ -0,0 +1,65 @@ +# Workflow timing analysis — Run 21477851525 + +**Run:** https://github.com/githubnext/gh-aw/actions/runs/21477851525 +**Workflow:** Smoke Copilot +**Total duration:** ~4m 57s (12:18:31 → 12:23:28 UTC) + +## 5‑stage summary (major stages) + +1. **Pre‑activation job** — **~7s** (12:18:35 → 12:18:42) + - Runner setup, sparse checkout of `actions/`, and setup action prep. + +2. **Activation job** — **~8s** (12:18:45 → 12:18:53) + - Second checkout + setup action execution to prepare activation assets. + +3. **Agent environment prep (containers + firewall + image pulls)** — **~1m 58s** (12:18:56 → 12:20:54) + - Agent job starts, pulls images, configures firewall, and starts containers. + +4. **Start coding agent (Claude Code CLI execution)** — **~1m 20s** (12:20:54.038 → 12:22:14.536) + - Copilot/Claude session runs, MCP clients connect, tests execute, and results produced. + +5. **Post‑agent pipeline** — **~53s** total + - Agent teardown: **~19s** (12:22:14 → 12:22:33) + - Threat detection job: **~24s** (12:22:36 → 12:23:00) + - Safe outputs + cache update (overlap): **~11s / 6s** (start 12:23:03) + - Conclusion job: **~9s** (12:23:18 → 12:23:27) + +## Time‑to‑Start (TTS) breakdown + +- **Workflow start → agent job start:** ~25s (12:18:31 → 12:18:56) + dominated by pre‑activation + activation jobs. + +- **Agent job start → Claude Code CLI start:** **~1m 58s** + dominated by image pulls, firewall setup, and container startup. + +- **Claude Code CLI session runtime:** **~1m 20s** + +## Top 5 improvements (highest leverage) + +1. **Pre‑warm or cache agent/container images** (Stage 3) + - Largest single block before the coding agent starts (~1m 58s). + - Use runner‑level image caching or a pre‑pull step (or a warm pool) to cut TTS materially. + +2. **Reduce activation/pre‑activation duplication** (Stages 1–2) + - Two similar jobs each do sparse checkout + setup action prep (~15s combined). + - Consolidate or cache activation assets to remove one job or shorten both. + +3. **Trim agent startup footprint** (Stage 3 → 4) + - MCP client startup and tool catalog loading is fast individually but adds latency. + - Disable unused toolsets for the smoke flow to reduce initialization work and token setup. + +4. **Shorten the Claude Code CLI session** (Stage 4) + - The session is ~1m 20s and includes extra tool calls (Playwright, build). + - For TTS focus, split “smoke” into a minimal “start‑agent” pass vs. the full workflow, + or defer heavy checks (Playwright/build) to post‑agent stages. + +5. **Parallelize or skip post‑agent jobs when possible** (Stage 5) + - Threat detection + safe outputs + conclusion add ~53s after agent completion. + - If safe/allowed, run detection in parallel with safe outputs or gate it to changed runs. + +## Key takeaways + +- **TTS is dominated by container/image prep** in the agent job (nearly 2 minutes). +- **Claude Code CLI execution is ~1m 20s** and is the next biggest block. +- **Pre‑activation + activation** add ~15s before the agent even starts. +- **Post‑agent jobs** add ~53s after the agent finishes, which matters for end‑to‑end time.