diff --git a/.github/skills/agentic-workflows/SKILL.md b/.github/skills/agentic-workflows/SKILL.md index 6fb19019416..141445630d5 100644 --- a/.github/skills/agentic-workflows/SKILL.md +++ b/.github/skills/agentic-workflows/SKILL.md @@ -71,6 +71,7 @@ Load these files from `github/gh-aw` (they are not available locally). - `.github/aw/test-coverage.md` - `.github/aw/test-expression.md` - `.github/aw/token-optimization-caching-budgets.md` +- `.github/aw/token-optimization-observability.md` - `.github/aw/token-optimization.md` - `.github/aw/triggers.md` - `.github/aw/update-agentic-workflow.md` diff --git a/.github/workflows/cgo.yml b/.github/workflows/cgo.yml index 0e2bbcacd28..a090e9b7f03 100644 --- a/.github/workflows/cgo.yml +++ b/.github/workflows/cgo.yml @@ -1359,10 +1359,10 @@ jobs: # legacy custom analyzer findings in tests or other analyzer families. # Note: -test=false intentionally scopes this gate to production code only. - name: Run custom linters - run: make golint-custom LINTER_FLAGS="-errstringmatch -panicinlibrarycode -manualmutexunlock -osexitinlibrary -rawloginlib -logfatallibrary -regexpcompileinfunction -fprintlnsprintf -strconvparseignorederror -jsonmarshalignoredeerror -uncheckedtypeassertion -fmterrorfnoverbs -tolowerequalfold -httpnoctx -timeafterleak -errortypeassertion -execcommandwithoutcontext -sprintfint -stringsindexcontains -stringscountcontains -bytesbufferstring -ioutildeprecated -mapclearloop -mapdeletecheck -sprintfbool -appendoneelement -timenowsub -stringsjoinone -writebytestring -lenstringsplit -stringreplaceminusone -osgetenvlibrary -ossetenvlibrary -stringsindexhasprefix -contextcancelnotdeferred -wgdonenotdeferred -goroutinemissingrecover -trimleftright -test=false" + run: make golint-custom LINTER_FLAGS="-errstringmatch -panicinlibrarycode -manualmutexunlock -osexitinlibrary -rawloginlib -logfatallibrary -regexpcompileinfunction -fprintlnsprintf -strconvparseignorederror -jsonmarshalignoredeerror -uncheckedtypeassertion -fmterrorfnoverbs -tolowerequalfold -httpnoctx -timeafterleak -errortypeassertion -execcommandwithoutcontext -sprintfint -stringsindexcontains -stringscountcontains -bytesbufferstring -ioutildeprecated -mapclearloop -mapdeletecheck -sprintfbool -appendoneelement -timenowsub -stringsjoinone -writebytestring -lenstringsplit -stringreplaceminusone -osgetenvlibrary -ossetenvlibrary -stringsindexhasprefix -contextcancelnotdeferred -wgdonenotdeferred -goroutinemissingrecover -trimleftright -walkfuncerrshadow -test=false" - name: Run custom linters (wasm) - run: GOOS=js GOARCH=wasm make golint-custom LINTER_FLAGS="-errstringmatch -panicinlibrarycode -manualmutexunlock -osexitinlibrary -rawloginlib -logfatallibrary -regexpcompileinfunction -fprintlnsprintf -strconvparseignorederror -jsonmarshalignoredeerror -uncheckedtypeassertion -fmterrorfnoverbs -tolowerequalfold -httpnoctx -timeafterleak -errortypeassertion -execcommandwithoutcontext -sprintfint -stringsindexcontains -stringscountcontains -bytesbufferstring -ioutildeprecated -mapclearloop -mapdeletecheck -sprintfbool -appendoneelement -timenowsub -stringsjoinone -writebytestring -lenstringsplit -stringreplaceminusone -osgetenvlibrary -ossetenvlibrary -stringsindexhasprefix -wgdonenotdeferred -goroutinemissingrecover -trimleftright -test=false" LINTER_PACKAGES="./pkg/console ./pkg/parser ./pkg/styles ./pkg/tty ./pkg/workflow" + run: GOOS=js GOARCH=wasm make golint-custom LINTER_FLAGS="-errstringmatch -panicinlibrarycode -manualmutexunlock -osexitinlibrary -rawloginlib -logfatallibrary -regexpcompileinfunction -fprintlnsprintf -strconvparseignorederror -jsonmarshalignoredeerror -uncheckedtypeassertion -fmterrorfnoverbs -tolowerequalfold -httpnoctx -timeafterleak -errortypeassertion -execcommandwithoutcontext -sprintfint -stringsindexcontains -stringscountcontains -bytesbufferstring -ioutildeprecated -mapclearloop -mapdeletecheck -sprintfbool -appendoneelement -timenowsub -stringsjoinone -writebytestring -lenstringsplit -stringreplaceminusone -osgetenvlibrary -ossetenvlibrary -stringsindexhasprefix -wgdonenotdeferred -goroutinemissingrecover -trimleftright -walkfuncerrshadow -test=false" LINTER_PACKAGES="./pkg/console ./pkg/parser ./pkg/styles ./pkg/tty ./pkg/workflow" # Ensure no action shell scripts invoke python or python3 - name: Lint action shell scripts diff --git a/docs/adr/49633-add-walkfuncerrshadow-linter-for-filepath-walk-err-shadow.md b/docs/adr/49633-add-walkfuncerrshadow-linter-for-filepath-walk-err-shadow.md index 3312c8bd30f..f8f9588b4b8 100644 --- a/docs/adr/49633-add-walkfuncerrshadow-linter-for-filepath-walk-err-shadow.md +++ b/docs/adr/49633-add-walkfuncerrshadow-linter-for-filepath-walk-err-shadow.md @@ -1,7 +1,7 @@ # ADR-49633: Add walkfuncerrshadow Linter for filepath.Walk/WalkDir err Parameter Shadowing **Date**: 2026-08-01 -**Status**: Draft +**Status**: Accepted **Deciders**: Unknown --- diff --git a/pkg/cli/compile_watch.go b/pkg/cli/compile_watch.go index cf53085a493..e5f8c92e5a7 100644 --- a/pkg/cli/compile_watch.go +++ b/pkg/cli/compile_watch.go @@ -79,7 +79,7 @@ func watchAndCompileWorkflows(ctx context.Context, markdownFile string, compiler } // Also watch subdirectories for include files (recursive watching) - err = filepath.Walk(workflowsDir, func(path string, info os.FileInfo, err error) error { + walkErr := filepath.Walk(workflowsDir, func(path string, info os.FileInfo, err error) error { if err != nil { return nil // Skip errors but continue walking } @@ -93,8 +93,8 @@ func watchAndCompileWorkflows(ctx context.Context, markdownFile string, compiler } return nil }) - if err != nil { - compileWatchLog.Printf("Failed to walk subdirectories: %v", err) + if walkErr != nil { + compileWatchLog.Printf("Failed to walk subdirectories: %v", walkErr) } // Always emit the begin pattern for task integration diff --git a/pkg/cli/data/agentic_workflows_fallback_aw_files.json b/pkg/cli/data/agentic_workflows_fallback_aw_files.json index 3d02da604cb..0b26d0d266b 100644 --- a/pkg/cli/data/agentic_workflows_fallback_aw_files.json +++ b/pkg/cli/data/agentic_workflows_fallback_aw_files.json @@ -55,6 +55,7 @@ "test-coverage.md", "test-expression.md", "token-optimization-caching-budgets.md", + "token-optimization-observability.md", "token-optimization.md", "triggers.md", "update-agentic-workflow.md", diff --git a/pkg/cli/dependency_graph.go b/pkg/cli/dependency_graph.go index d7b1e8f6ed2..41f810a9e15 100644 --- a/pkg/cli/dependency_graph.go +++ b/pkg/cli/dependency_graph.go @@ -63,7 +63,7 @@ func (g *DependencyGraph) BuildGraph(compiler *workflow.Compiler) error { // Find all markdown files in the workflows directory (including subdirectories) var allWorkflows []string - err := filepath.Walk(g.workflowsDir, func(path string, info os.FileInfo, err error) error { + walkErr := filepath.Walk(g.workflowsDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -72,8 +72,8 @@ func (g *DependencyGraph) BuildGraph(compiler *workflow.Compiler) error { } return nil }) - if err != nil { - return fmt.Errorf("failed to scan workflows directory: %w", err) + if walkErr != nil { + return fmt.Errorf("failed to scan workflows directory: %w", walkErr) } depGraphLog.Printf("Found %d workflow files to analyze", len(allWorkflows)) diff --git a/pkg/cli/logs_download.go b/pkg/cli/logs_download.go index cb7ebc16c20..715ab2f66b6 100644 --- a/pkg/cli/logs_download.go +++ b/pkg/cli/logs_download.go @@ -189,7 +189,7 @@ func findArtifactDir(outputDir, baseName string, legacyName string) string { // case). label is used in log and user-facing messages. // Cleanup failures are non-fatal: they are logged (and optionally printed) but do not return an error. func flattenArtifactTree(sourceDir, artifactDir, outputDir, label string, verbose bool) error { - err := filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error { + walkErr := filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -231,8 +231,8 @@ func flattenArtifactTree(sourceDir, artifactDir, outputDir, label string, verbos return nil }) - if err != nil { - return fmt.Errorf("failed to flatten %s: %w", label, err) + if walkErr != nil { + return fmt.Errorf("failed to flatten %s: %w", label, walkErr) } // Remove the now-empty artifact directory structure. @@ -502,7 +502,7 @@ func extractZipFile(f *zip.File, destDir string, verbose bool) (extractErr error func listArtifacts(outputDir string) ([]string, error) { var artifacts []string - err := filepath.Walk(outputDir, func(path string, info os.FileInfo, err error) error { + walkErr := filepath.Walk(outputDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -522,8 +522,8 @@ func listArtifacts(outputDir string) ([]string, error) { return nil }) - if err != nil { - return nil, err + if walkErr != nil { + return nil, walkErr } return artifacts, nil diff --git a/pkg/cli/logs_metrics.go b/pkg/cli/logs_metrics.go index 3ea7591abc0..1f24615000d 100644 --- a/pkg/cli/logs_metrics.go +++ b/pkg/cli/logs_metrics.go @@ -158,7 +158,7 @@ func extractLogMetrics(logDir string, verbose bool, workflowPath ...string) (Log // Try events.jsonl first – it provides a precise, structured event list from the Copilot CLI // session state and is the most reliable source for tool calls, turns, and usage metrics. // Fall back to walking .log files if events.jsonl is not present or cannot be parsed. - var err error + var walkErr error eventsJSONLParsed := false if eventsJSONLPath := findEventsJSONLFile(logDir); eventsJSONLPath != "" { if verbose { @@ -185,7 +185,7 @@ func extractLogMetrics(logDir string, verbose bool, workflowPath ...string) (Log // Walk through all .log files when events.jsonl was not available or failed to parse if !eventsJSONLParsed { - err = filepath.Walk(logDir, func(path string, info os.FileInfo, err error) error { + walkErr = filepath.Walk(logDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -256,7 +256,7 @@ func extractLogMetrics(logDir string, verbose bool, workflowPath ...string) (Log logsMetricsLog.Printf("Metrics extraction completed: tokens=%d, cost=%.4f, turns=%d", metrics.TokenUsage, metrics.EstimatedCost, metrics.Turns) } - return metrics, err + return metrics, walkErr } // ExtractLogMetricsFromRun extracts log metrics from a processed run's log directory @@ -678,7 +678,7 @@ func extractMCPFailuresFromRun(runDir string, run WorkflowRun, verbose bool, exp // Look for agent output logs that contain the system init entry with MCP server status // This information is available in the raw log files, typically with names containing "log" - err := filepath.Walk(runDir, func(path string, info os.FileInfo, err error) error { + walkErr := filepath.Walk(runDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -709,8 +709,8 @@ func extractMCPFailuresFromRun(runDir string, run WorkflowRun, verbose bool, exp return nil }) - if err != nil { - return mcpFailures, fmt.Errorf("error walking run directory: %w", err) + if walkErr != nil { + return mcpFailures, fmt.Errorf("error walking run directory: %w", walkErr) } if verbose && len(mcpFailures) > 0 { diff --git a/pkg/cli/remove_command.go b/pkg/cli/remove_command.go index f1b0f5dc5a6..8c0263eff27 100644 --- a/pkg/cli/remove_command.go +++ b/pkg/cli/remove_command.go @@ -219,7 +219,7 @@ func cleanupOrphanedIncludes(verbose bool) error { workflowsDir := constants.GetWorkflowDir() var allIncludes []string - err = filepath.Walk(workflowsDir, func(path string, info os.FileInfo, err error) error { + walkErr := filepath.Walk(workflowsDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -240,8 +240,8 @@ func cleanupOrphanedIncludes(verbose bool) error { return nil }) - if err != nil { - return fmt.Errorf("failed to scan include files: %w", err) + if walkErr != nil { + return fmt.Errorf("failed to scan include files: %w", walkErr) } // Remove unused includes @@ -339,7 +339,7 @@ func getAllIncludeFiles() ([]string, error) { workflowsDir := constants.GetWorkflowDir() var allIncludes []string - err := filepath.Walk(workflowsDir, func(path string, info os.FileInfo, err error) error { + walkErr := filepath.Walk(workflowsDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -360,14 +360,14 @@ func getAllIncludeFiles() ([]string, error) { return nil }) - return allIncludes, err + return allIncludes, walkErr } // cleanupAllIncludes removes all include files when no workflows remain func cleanupAllIncludes(verbose bool) error { workflowsDir := constants.GetWorkflowDir() - err := filepath.Walk(workflowsDir, func(path string, info os.FileInfo, err error) error { + walkErr := filepath.Walk(workflowsDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -391,7 +391,7 @@ func cleanupAllIncludes(verbose bool) error { return nil }) - return err + return walkErr } // findIncludesInContent finds all import references in content diff --git a/pkg/cli/trial_support.go b/pkg/cli/trial_support.go index e906e95d33c..b791dbe963d 100644 --- a/pkg/cli/trial_support.go +++ b/pkg/cli/trial_support.go @@ -55,7 +55,7 @@ func downloadAllArtifacts(hostRepoSlug, runID string, verbose bool) (*TrialArtif } // Walk through all downloaded artifacts - err = filepath.Walk(tempDir, func(path string, info os.FileInfo, err error) error { + walkErr := filepath.Walk(tempDir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } @@ -111,9 +111,9 @@ func downloadAllArtifacts(hostRepoSlug, runID string, verbose bool) (*TrialArtif return nil }) - if err != nil { + if walkErr != nil { if verbose { - fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Error walking artifact directory: %v", err))) + fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Error walking artifact directory: %v", walkErr))) } }