Repository navigation
[repository-quality] Repository Quality Improvement: Audit Goroutine Context Blindness & API Call Cancellability (2026-07-20) #46848
Closed
Replies: 1 comment
|
This discussion was automatically closed because it expired on 2026-07-21T13:36:24.954Z.
|
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.
🎯 Repository Quality Improvement Report - Audit Goroutine Context Blindness & API Call Cancellability
Analysis Date: 2026-07-20
Focus Area: Audit Goroutine Context Blindness —
collectAuditAnalysisResultsdrops callerctxStrategy Type: Custom
Custom Area: Yes — the audit analysis fan-out pattern makes network calls in goroutines that have no access to the caller's
context.Context, so they cannot be cancelled. This is a concrete, repo-specific gap discovered through direct code inspection.Executive Summary
AuditWorkflowRunreceives acontext.Contextfrom the CLI (and honours it before and after analysis), but it silently drops it at the boundary tocollectAuditAnalysisResults. That function fans out 13–15 goroutines using a rawsync.WaitGroup; none of those goroutines hold a reference toctx. Among them,fetchJobDetailsWithCountscallsworkflow.RunGHCombined— the context-free overload of a function that has aRunGHCombinedContextcounterpart — making a live GitHub API call that cannot be cancelled by user interrupt, process signal, or deadline.The project already demonstrates the correct pattern:
pkg/cli/mcp_inspect_inspector.gouseserrgroup.WithContext(ctx), andworkflow.RunGHCombinedContextexists precisely to pass context through to the underlyingexec.CommandContext. The gap is that audit analysis was built before those patterns were established and has never been back-ported.Two secondary issues compound the primary one:
collectAuditAnalysisResultsdoes not propagate errors from goroutines to the caller (all goroutine failures are swallowed), and theauditAnalysisResultsstruct relies on undocumented field-ownership assumptions for goroutine-safety rather than explicit documentation or structural enforcement.Full Analysis Report
Focus Area: Audit Goroutine Context Blindness & API Call Cancellability
Current State Assessment
Metrics Collected:
collectAuditAnalysisResultsctxctxfetchJobDetailsWithCounts)errgroup.WithContextin productionpkg/mcp_inspect_inspector.go)RunGHCombinedContextcallers inpkg/cli/audit.goRunGHCombined(context-free) calls inpkg/cli/audit cluster//go:build !integrationonaudit_test.goFindings
Strengths
workflow.RunGHCombinedContext(ctx, ...)already exists and is well-documented; migration is a one-line call-site change per function.errgroup(golang.org/x/sync) is already a direct dependency; no new deps needed.runAuditAnalysis[T]generic helper encapsulates the goroutine dispatch pattern cleanly — addingctxto it is a localised change that ripples to all 10+ call sites automatically.ensureAuditNotCancelledshows the author already thought about context cancellability, just not inside the concurrent analysis phase.Areas for Improvement
collectAuditAnalysisResultsdrops the caller'sctxentirely; goroutines cannot respond to cancellation signals.fetchJobDetailsWithCountscallsRunGHCombined(usescontext.Background()), notRunGHCombinedContext; a slow GitHub API response blocks the entire audit indefinitely.runAuditAnalysis[T]swallows all goroutine errors; there is no way for the caller to distinguish "all goroutines succeeded" from "5 goroutines silently failed".auditAnalysisResultshas no mutex and no documentation explaining that field-ownership prevents data races; a future refactor could introduce a race silently.collectAuditAnalysisResultssignature does not acceptctx, meaning even if inner helpers were updated, the outer shell would still be context-blind without a signature change.Detailed Analysis
Call graph from
AuditWorkflowRunto the context gap:Correct pattern already in use (
mcp_inspect_inspector.go):Existing
RunGHCombinedContextthat should be used:fetchJobDetailsWithCountsneeds only to acceptctx context.Contextand replaceworkflow.RunGHCombined(...)withworkflow.RunGHCombinedContext(ctx, ...).🤖 Tasks for Copilot Agent
NOTE TO PLANNER AGENT: Split the following tasks into individual work items.
Improvement Tasks
Task 1: Add
ctxtocollectAuditAnalysisResultsand its launch helpersPriority: High
Estimated Effort: Medium
Focus Area: Goroutine Context Blindness
Description: Add
ctx context.Contextas the first parameter tocollectAuditAnalysisResults,launchCoreAuditAnalyses,launchMetricsAnalysis,launchJobDetailsAnalysis,launchFirewallAuditAnalyses,launchFirewallAnalysis, andlaunchSupplementalAuditAnalysesinpkg/cli/audit.go. Pass throughctxfrom the call site inAuditWorkflowRun. This unblocks all follow-on context propagation tasks.Acceptance Criteria:
collectAuditAnalysisResults(ctx, run, ...)compiles and the single call site inaudit.go:373passesctxlaunch*helpers accept and forwardctxrunAuditAnalysis[T]generic helper also acceptsctx context.Contextas first parametermake buildpasses; no new linter errorsCode Region:
pkg/cli/audit.go— functionscollectAuditAnalysisResults,launchCoreAuditAnalyses,launchMetricsAnalysis,launchJobDetailsAnalysis,launchFirewallAuditAnalyses,launchFirewallAnalysis,launchSupplementalAuditAnalyses,runAuditAnalysisTask 2: Replace
RunGHCombinedwithRunGHCombinedContextinfetchJobDetailsWithCountsPriority: High
Estimated Effort: Small
Focus Area: Goroutine Context Blindness
Description:
fetchJobDetailsWithCountsinpkg/cli/logs_github_api.go(line 79) callsworkflow.RunGHCombined, which internally usescontext.Background(). After Task 1 addsctxtolaunchJobDetailsAnalysis, thread thatctxall the way to theRunGHCombinedcall and replace it withworkflow.RunGHCombinedContext(ctx, ...). This makes the GitHub API call cancellable.Acceptance Criteria:
fetchJobDetailsWithCounts(ctx context.Context, runID int64, verbose bool)signature updatedlaunchJobDetailsAnalysisupdated to passctxworkflow.RunGHCombined(...)at line 85 replaced withworkflow.RunGHCombinedContext(ctx, ...)make buildpassesCode Region:
pkg/cli/logs_github_api.go:79–120,pkg/cli/audit.go:716–730Task 3: Migrate
collectAuditAnalysisResultsfromsync.WaitGrouptoerrgroup.WithContextPriority: Medium
Estimated Effort: Medium
Focus Area: Goroutine Context Blindness & Error Propagation
Description: Replace the raw
sync.WaitGroupincollectAuditAnalysisResultswitherrgroup.WithContext(ctx)(golang.org/x/sync/errgroup, already a dependency). Pass the derivedgctxinto alllaunch*helpers so that a cancellation or deadline expiry on the originalctxstops all goroutines. UpdaterunAuditAnalysis[T]to returnerror(or accept anerrgroup.Group) so errors can surface. Sinceerrgroupstops at first error, keep the current soft-failure behaviour by logging errors inside goroutines and returningniltoerrgroup(non-fatal errors), while returning the goroutine contextgctxerror for hard cancellation.Acceptance Criteria:
golang.org/x/sync/errgroupimport added toaudit.go(already ingo.mod)errgroup.WithContextused incollectAuditAnalysisResultsgctxgh aw auditterminates the goroutines promptlymake buildandmake testpassCode Region:
pkg/cli/audit.go:656–805Task 4: Document goroutine-safety invariants on
auditAnalysisResultsPriority: Medium
Estimated Effort: Small
Focus Area: Code Documentation & Race Safety
Description: The
auditAnalysisResultsstruct (line 329 ofaudit.go) is written by multiple concurrent goroutines without a mutex, which is safe only because each goroutine owns exactly one field and no field is shared. This invariant is invisible to future readers and maintainers. Add a comment on the struct declaration and a comment on eachlaunch*function stating which fields it writes. Optionally, add a//go:buildgatedsync/atomicassertion or a-race-gated test.Acceptance Criteria:
auditAnalysisResultsstruct has a doc comment explaining the field-ownership concurrency modellaunch*helper function has a one-line comment naming which fields it exclusively writesmake buildpassesCode Region:
pkg/cli/audit.go:329–360,pkg/cli/audit.go:668–790Task 5: Extend
RunGHCombinedContextadoption to other context-free callers in the audit clusterPriority: Low
Estimated Effort: Medium
Focus Area: Context Propagation & API Cancellability
Description: Several other functions in the audit cluster currently call
workflow.RunGHCombinedwithout context:pkg/cli/logs_rate_limit.go:63(analyzeRateLimits) andpkg/cli/logs_github_api.go(called from audit goroutines). After Tasks 1–2 establishctxthreading, identify all callers within the audit code path that useRunGHCombinedand migrate them toRunGHCombinedContext. Usegrep -rn "RunGHCombined\b" pkg/cli/to enumerate candidates, then check which are reachable fromcollectAuditAnalysisResults.Acceptance Criteria:
grep -rn "RunGHCombined\b" pkg/cli/shows no call reachable from the audit analysis fan-out that uses the context-free overloadctx context.Contextmake buildpassesCode Region:
pkg/cli/logs_rate_limit.go,pkg/cli/logs_github_api.go, any other files reached fromcollectAuditAnalysisResults📊 Historical Context
Previous Focus Areas
🎯 Recommendations
Immediate Actions (This Week)
ctxparameter threading tocollectAuditAnalysisResults(Task 1) — Priority: High; zero behaviour change, pure mechanical refactorRunGHCombinedwithRunGHCombinedContextinfetchJobDetailsWithCounts(Task 2) — Priority: High; one-line change that fixes a real cancellability holeShort-term Actions (This Month)
errgroup.WithContext(Task 3) — Priority: Medium; structural improvement enabling proper error propagationLong-term Actions (This Quarter)
RunGHCombinedContextadoption across audit call paths (Task 5) — Priority: Low; comprehensive cleanup after the pattern is established📈 Success Metrics
RunGHCombinedcalls in audit fan-out: 1+ → 0auditAnalysisResultsconcurrency model documented: No → YesNext Steps
References:
[§29746354208]Generated by Repository Quality Improvement Agent
All reactions