feat(0.2): per-detector wall-clock timeout budgets (Track 9.4)#154
Open
feat(0.2): per-detector wall-clock timeout budgets (Track 9.4)#154
Conversation
Adds the per-detector budget mechanism that protects analyze runs
from any single hung detector blocking the whole pipeline.
The mechanism
- New `DetectorMeta.Budget time.Duration` field. Zero means "use
DefaultDetectorBudget" (30 seconds). Detectors with legitimate
long-running work set this explicitly.
- New `safeDetectWithBudget(reg, fn)` wrapper composes with the
existing safeDetect panic-recovery: a panicking detector still
produces the detectorPanic marker; a slow detector produces the
new detectorBudgetExceeded marker.
- All call sites in detector_registry.go (Run + RunWithGraph
Phase 1/2/3 paths) routed through the budget wrapper. Pre-Track-
9.4 a hung detector would block the goroutine waiting on
wg.Wait(); now the budget elapses first and the wait completes.
- New SignalDetectorBudgetExceeded type registered in the manifest
+ signal catalog so ValidateSnapshot accepts the marker (same
posture as detectorPanic — without the catalog entry, a single
budget overrun would invalidate the whole snapshot).
Behavior contract
When a detector exceeds its budget, the pipeline returns the
budget-exceeded marker instead of waiting for the eventual
completion. The detector goroutine completes in the background
(Go has no goroutine kill primitive); its post-budget signals are
discarded. This is the right trade-off for the failure modes the
budget targets: runaway regex, accidentally-O(n²) graph walks,
blocking I/O on a slow filesystem.
Coverage
Five new tests in detector_budget_test.go:
- budget exceeded → marker returned within budget window
- fast detector → original signals returned
- zero budget → DefaultDetectorBudget applied
- panic + budget compose correctly (detectorPanic wins)
- registry-level integration: r.Run() returns within budget
even when a registered detector deliberately sleeps past it
Plus the regenerated docs/rules/engine/detector-budget.md doc.
Verification: all 5 budget tests pass; full Go test suite green;
make docs-verify clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Terrain AI Risk Review
Decision: PASS — AI surfaces are covered. |
[RISK] Terrain — Merge with caution
Coverage gaps in changed code
8 pre-existing issues on changed files
Recommended tests8 test(s) with exact coverage of 22 impacted unit(s). 11 impacted unit(s) have no covering tests in the selected set.
AI Risk Review
2 advisory findings
Owners: PMCLSF Limitations
Generated by Terrain · Targeted Test ResultsTerrain selected 8 test(s) instead of the full suite.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the per-detector budget mechanism that protects analyze runs
from a single hung detector blocking the whole pipeline. When a
detector exceeds its budget, the pipeline returns a marker signal
and moves on — the rest of the run completes normally.
What changed
DetectorMeta.Budget time.Duration— zero meansDefaultDetectorBudget (30s). Detectors with legitimate
long-running work set this explicitly.
safeDetectWithBudget(reg, fn)wrapper composes withexisting safeDetect panic-recovery. All 9 call sites in
detector_registry.go routed through it.
manifest + signal catalog so ValidateSnapshot accepts the
marker (same posture as detectorPanic).
docs/rules/engine/detector-budget.mdregenerated.Behavior contract
When budget elapses:
primitive); its post-budget signals are discarded
rather than blocking on the slow detector
This is the right trade-off for the failure modes targeted:
runaway regex, accidentally-O(n²) graph walks, blocking I/O.
Test plan
detector_budget_test.go:budget-exceeded, fast-passes, zero-uses-default, panic+budget
compose, registry-level integration
go test ./...— full suite greenmake docs-verify— generated docs currentPlan tracker
Closes Track 9.4. Track 9 remaining: 9.1 (capability metadata),
9.2 (panic recovery completion), 9.3 (missing-input diagnostics),
9.5 (pipeline architectural separation), 9.6 (registry refactor),
9.7 (truth-verify). All explicitly post-0.2.0-blocking.
🤖 Generated with Claude Code