feat(evaluator): run-lifetime windowed aggregations (over run)#15
Merged
Conversation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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
over runto the windowed-condition grammar — windows bounded by the run's lifetime instead of wall-clock. Composes orthogonally withmode: end-of-run: window boundary and firing trigger are independent. Backward compatible with all existingover Nmrules.Behavior matrix
mode: end-of-run?over 5mover 5mover runover runExample
Implementation
The behavioral change is "skip eviction" — a
runBounded boolflag onRingBuffershort-circuitsevict(). The flag threads from condition parser →windowedLeaf→getOrCreateBuffer→NewRingBuffer→RingBuffer.runBounded→evictno-op. Snapshot/restore round-trips the flag via additive JSON field (`omitempty` keeps old snapshots backward-compatible).Touched files
Production: `internal/evaluator/condition.go`, `ringbuffer.go`, `engine.go`, `state.go`, `docs/configuration.md`, `ding.yaml.example`.
Tests: `evaluator_test.go`, `end_of_run_test.go`, `state_test.go`, `internal/cli/test_rule_test.go`.
Cooldown caveat (documented)
Aggregates like `count` are monotonically non-decreasing under `over run` — once the threshold crosses, the rule will fire on every subsequent matching event without `mode: end-of-run` or a `cooldown:`. The docs guide users to pair mid-run `over run` rules with one of these. The engine doesn't enforce.
`ding serve` mode
`over run` is supported syntactically; semantically it means "since daemon start" (buffer accumulates indefinitely, capped by `max_buffer_size`). Documented as wedge-mode-recommended; not validated/rejected because the same config file may be used for both `ding run` and `ding serve`.
Test plan
Note on event-time vs wall-clock semantics
`RingBuffer.Add` calls `evict(at)` with the event's own timestamp, not wall-clock `now`. This means `over Nm` evicts entries older than `(most recent event's timestamp) - N minutes` — event-time, not receipt-time. This is pre-existing behavior unchanged by this PR. For `over run` it's moot since eviction is disabled.
Roadmap
Item 2 of 4 in the inward-polish sequence (post-CircleCI pivot). Item 1 (rule playground / dry-run) shipped in PR #14. Items 3 (recipe sweep) and 4 (template helpers) follow.
🤖 Generated with Claude Code