[repository-quality] Repository Quality Improvement Report - Panic-in-Library-Code Governance Gaps (2026-07-29) #48896
Closed
Replies: 2 comments
|
This discussion was automatically closed because it expired on 2026-07-30T13:31:57.585Z.
|
0 replies
|
This discussion was automatically closed because it expired on 2026-07-30T13:31:57.585Z.
|
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 - Panic-in-Library-Code Governance Gaps
Analysis Date: 2026-07-29
Focus Area: Panic Usage in Library Code — Linter Contract Enforcement & sync.OnceValue Blind Spot
Strategy Type: Custom
Custom Area: Yes — the repo has a purpose-built
panicinlibrarycodeanalyzer (pkg/linters/panic-in-library-code) enforcing "no panic in pkg/, return errors instead" with narrow, auditable exemptions (BUG-prefixed messages,init(),sync.Once.Doclosures, and documented "panics on/if" contracts). No prior run has audited whether the exemption mechanism itself has coverage gaps or whether existing exempted panics are actually reachable from external/user-controlled input, which would violate the linter's own intent.Executive Summary
The
panicinlibrarycodeanalyzer (pkg/linters/panic-in-library-code/panic-in-library-code.go) is well-designed but has one structural blind spot: itsisInSyncOnceDoFuncLitexemption only recognizes the classicsync.Once.Do(func(){...})pattern (matched via aDoselector call), not the newersync.OnceValue(func() T {...})/sync.OnceFuncidioms introduced in Go 1.21. The repo already usessync.OnceValuein three places (pkg/workflow/engine_definition_loader.go:41,pkg/workflow/permissions_toolset_data.go:39,pkg/console/console.go:25), two of which contain or could contain panics. Today those panics happen to pass only because their messages start with"BUG:"— an incidental, not principled, protection. Any futuresync.OnceValueinitializer that panics without aBUG:prefix (e.g., propagating a rawerr) will silently bypass linting despite being architecturally identical to the already-exemptedsync.Once.Dopattern.A second finding: of the ~14 non-test, non-testdata panics remaining in pkg/, several (
pkg/workflow/model_aliases.go:96,pkg/workflow/claude_tools.go:147,pkg/actionpins/actionpins.go:267) rely on theBUG:-prefix exemption or the documented-contract exemption inconsistently — some have the required "Panics on/if" doc phrase, others don't, making it unpredictable which panics are "sanctioned" vs. accidentally passing. Two panics inpkg/actionpins/actionpins.go(empty-SHA guards) are reachable indirectly from dynamically-resolved action SHAs and hardcoded pin data — plausible defense-in-depth for release-time data corruption, but not clearly bounded to build-time-only invocation, so a corrupted or maliciousaction_pins.jsonmerged via PR could crash the compiler for any workflow. Tightening the analyzer's Once-family detection, standardizing doc-contract phrasing, and adding tests that assertsync.OnceValue/sync.OnceFuncclosures are correctly linted will close a real enforcement gap in an otherwise strong control the team already invested in.Full Analysis Report
Focus Area: Panic Usage in Library Code — Linter Contract Enforcement
Current State Assessment
The custom
panicinlibrarycodeGo analyzer runs as part of the linter suite (registered inpkg/linters/registry.go:107, documented inpkg/linters/doc.go:38, spec-tested inpkg/linters/spec_test.go:136). It flagspanic()in anypkg/package (packages undercmd/or namedmainare exempt), with four narrow exemptions: message starts withBUG:, call is insideinit(), call is inside async.Once.Do(func(){...})closure, or the enclosing function's doc comment contains "panics on/if" / "panic on/if".Metrics Collected:
sync.OnceValue/OnceFuncusages repo-widepermissions_toolset_data.go,model_aliases.go'ssync.Once.Do— 1 covered, 1 not)sync.OnceValue/OnceFuncclosurespanicinlibrarycoderegistered + spec-testedFindings
Strengths
pkg/linters/registry.goand covered bypkg/linters/spec_test.goanddoc_sync_test.go, so the linter itself won't silently disappear from CI.pkg/workflow/domains.go:740,pkg/workflow/permissions_toolset_data.go:44) correctly signal internal-invariant violations rather than expected error paths.Areas for Improvement
isInSyncOnceDoFuncLitinpkg/linters/panic-in-library-code/panic-in-library-code.go:95-115only matchesX.Do(func(){...})calls (checkssel.Sel.Name == "Do"andisSyncOnceType). It does not recognizesync.OnceValue(func() T {...})orsync.OnceFunc(func(){...}), both stdlib idioms the repo already uses (pkg/workflow/engine_definition_loader.go:41,pkg/workflow/permissions_toolset_data.go:39). Panics inside these closures currently pass only incidentally via theBUG:-prefix exemption; a future contributor addingpanic(err)(noBUG:prefix) inside async.OnceValueinitializer would slip past the linter entirely.pkg/actionpins/actionpins.go:265(FormatPinnedActionReference) andpkg/workflow/cache.go:42(cacheMemoryDirFor) both have proper "Panics if/on ..." doc comments and are legitimately exempted, butpkg/workflow/claude_tools.go:141(prepareClaudeToolsForAllowedList) has no doc comment at all — it passes only via theBUG:message prefix, meaning any future refactor that rewords the panic message without noticing the missing doc contract would break silently (the panic would then be flagged, or worse, would remain unflagged for the wrong reason).pkg/actionpins/actionpins.go:166(loadActionPinsData) and:174panic on malformed/empty-SHA embeddedaction_pins.jsondata. This is defensible as build-time data integrity checking, but the function has no explicit "only called during package init" guarantee in code (it's invoked viagetActionPins()→sync.OnceValue-style caching perpkg/actionpins/actionpins.go:128) — worth confirming test coverage exists proving this can only execute against the embedded, CI-validated JSON and never a runtime-supplied path.pkg/linters/panic-in-library-code/panic-in-library-code_test.go's testdata exercises async.OnceValue/sync.OnceFuncclosure case, so the gap above (High finding) has no regression test guarding it either way.Detailed Analysis
The core risk is asymmetry between exemption intent and exemption implementation. The linter's
sync.Once.Doexemption exists becauseDoclosures execute exactly once, typically during lazy initialization of program-invariant data (embedded JSON, computed constants) — a legitimatepanic-on-corruption use case distinct from ordinary request/compile-time error handling.sync.OnceValue/sync.OnceFunc(Go 1.21+) are direct successors to that exact pattern and are already used for equivalent purposes in this codebase (loadBuiltinEngineDefinitionMap,getToolsetPermissionsMap). ExtendingisInSyncOnceDoFuncLit(or adding a sibling check, e.g.isInOnceValueOrOnceFuncArg) to detectsync.OnceValue(<FuncLit>)/sync.OnceFunc(<FuncLit>)call patterns closes this gap cleanly, using the samecur.Enclosing+ parent*ast.CallExprtraversal already present in the function, just checking the callee identifier/selector against"OnceValue"/"OnceFunc"package-qualified names instead of a.Doselector.🤖 Tasks for Copilot Agent
NOTE TO PLANNER AGENT: Split the following tasks into individual work items.
Improvement Tasks
Task 1: Extend panicinlibrarycode analyzer to recognize sync.OnceValue/OnceFunc closures
Priority: High
Estimated Effort: Small
Focus Area: Panic Usage in Library Code
Description: The
isInSyncOnceDoFuncLithelper inpkg/linters/panic-in-library-code/panic-in-library-code.goonly exempts panics insidesync.Once.Do(func(){...})closures. Add detection forsync.OnceValue(func() T {...})andsync.OnceFunc(func(){...})call expressions so panics inside these lazy-initializer closures are treated the same way (an intentional, documented pattern), preventing accidental future bypasses that currently rely only on incidentalBUG:-prefix matching.Acceptance Criteria:
isInOnceValueOrOnceFuncArg) detectssync.OnceValue(<FuncLit>)andsync.OnceFunc(<FuncLit>)patterns analogous to the existingDo-selector check.shouldSkipPanicwires in the new helper alongsideisInSyncOnceDoFuncLit.pkg/linters/panic-in-library-code/testdata/exercising both an exemptedsync.OnceValuepanic and a non-exempted ordinary panic for regression coverage.go test ./pkg/linters/panic-in-library-code/...passes.Code Region:
pkg/linters/panic-in-library-code/panic-in-library-code.go(lines 88-132)Task 2: Add missing "Panics if" doc contract to prepareClaudeToolsForAllowedList
Priority: Medium
Estimated Effort: Small
Focus Area: Panic Usage in Library Code
Description:
pkg/workflow/claude_tools.go:141(prepareClaudeToolsForAllowedList) panics with aBUG:-prefixed message but has no doc comment describing the panic contract, unlike comparable functions such asFormatPinnedActionReferenceandcacheMemoryDirFor. Add a doc comment following the established "Panics if ..." convention so the panic contract is discoverable from documentation alone, independent of the incidentalBUG:message-prefix exemption.Acceptance Criteria:
prepareClaudeToolsForAllowedListexplaining when/why it panics, using the phrase "panics if" or "panics on" to align with the linter's documented-contract exemption.make fmtrun after edit.Code Region:
pkg/workflow/claude_tools.go(lines 141-148)Task 3: Verify build-time-only reachability of action_pins.json panic paths
Priority: Medium
Estimated Effort: Medium
Focus Area: Panic Usage in Library Code
Description:
loadActionPinsDatainpkg/actionpins/actionpins.go:162-175panics on JSON unmarshal failure or empty-SHA entries in the embeddedaction_pins.json. Confirm (via test or code comment/assertion) that this path is only ever exercised against the compile-time embedded file and never against any runtime/user-supplied data, and add a regression test that a corruptedaction_pins.json(if ever merged) would be caught by an existing CI check (e.g.,go generate/go vet/dedicated validation test) before reaching production, rather than only failing at panic time in a user's compiler run.Acceptance Criteria:
loadActionPinsDatais only called with the//go:embed-sourcedaction_pins.json, never a caller-supplied byte slice.action_pins.jsonhas no empty SHAs / is valid JSON, independent of the panic path, so bad data is caught at PR time, not at every compiler invocation.loadActionPinsDataif reachability is confirmed safe, or file a follow-up if a gap is found.Code Region:
pkg/actionpins/actionpins.go(lines 128-176)📊 Historical Context
Previous Focus Areas
🎯 Recommendations
Immediate Actions (This Week)
isInSyncOnceDoFuncLitto coversync.OnceValue/sync.OnceFunc— Priority: HighShort-term Actions (This Month)
action_pins.jsonpanic paths — Priority: MediumLong-term Actions (This Quarter)
📈 Success Metrics
Next Steps
Generated by Repository Quality Improvement Agent
Next analysis: 2026-07-30 — Focus area selected by diversity algorithm
Warning
Firewall blocked 1 domain
The following domain was blocked by the firewall during workflow execution:
proxy.golang.orgTo allow these domains, add them to the
network.allowedlist in your workflow frontmatter:See Network Configuration for more information.
All reactions