Skip to content

feat(evaluator): run-lifetime windowed aggregations (over run)#15

Merged
zuchka merged 8 commits into
mainfrom
add-run-lifetime-windowing
May 8, 2026
Merged

feat(evaluator): run-lifetime windowed aggregations (over run)#15
zuchka merged 8 commits into
mainfrom
add-run-lifetime-windowing

Conversation

@zuchka
Copy link
Copy Markdown
Collaborator

@zuchka zuchka commented May 8, 2026

Summary

Adds over run to the windowed-condition grammar — windows bounded by the run's lifetime instead of wall-clock. Composes orthogonally with mode: end-of-run: window boundary and firing trigger are independent. Backward compatible with all existing over Nm rules.

Behavior matrix

condition window mode: end-of-run? result
over 5m no wall-clock sliding (today)
over 5m yes aggregate of last 5m of run, fires at exit (today)
over run no run-bounded sliding, fires mid-run when threshold crosses
over run yes whole-run aggregate, fires once at exit

Example

rules:
  # Whole-run aggregate, fires once at exit
  - name: high_avg_mem
    match: { metric: mem_pct }
    condition: avg(value) over run > 80
    mode: end-of-run
    message: "avg memory was {{ .avg }}% across the run"

  # Run-bounded sliding window, fires mid-run on threshold cross
  - name: errors_pile_up
    match: { metric: errors }
    condition: count(value) over run > 10
    cooldown: 30s

Implementation

The behavioral change is "skip eviction" — a runBounded bool flag on RingBuffer short-circuits evict(). The flag threads from condition parser → windowedLeafgetOrCreateBufferNewRingBufferRingBuffer.runBoundedevict no-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

  • `go test ./internal/evaluator/...` passes
  • `go test ./internal/cli/...` passes
  • `go test -race ./...` passes
  • End-to-end smoke via `ding test-rule` with an `over run` + `mode: end-of-run` rule fires once with the correct aggregate
  • Sibling test added to lock down wall-clock eviction (closes a coverage gap from PR feat: rule playground (ding test-rule + ding run --dry-run) #14's item-1 windowed test)

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

zuchka and others added 8 commits May 8, 2026 15:21
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>
@zuchka zuchka merged commit 057a46a into main May 8, 2026
1 check passed
@zuchka zuchka deleted the add-run-lifetime-windowing branch May 8, 2026 23:33
@github-actions github-actions Bot locked and limited conversation to collaborators May 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant