Ticket 102 (splitting the engine into reader/primary/profile actors) was a single multi-thousand-line ticket covering a new actor topology, an EngineHandle API redesign, a query fan-out layer, moving alerts, and a full test-harness rewrite, with some design decisions deliberately left open. Splitting it into six sequenced tickets (profile actor skeleton + signaling, move metric computation, move alert evaluation, split the handle API, query fan-out, test harness rework) lets each land as its own reviewable commit instead of one large speculative rewrite. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TeyhRfMYjMditek6geDRoQ
53 lines
2.5 KiB
Markdown
53 lines
2.5 KiB
Markdown
# 105 — Move alert evaluation onto the profile actor
|
|
|
|
## Summary
|
|
|
|
Alert rules are profile-scoped and read metric buckets
|
|
(`alert_states: HashMap<String, AlertRuntimeState>` lives on the
|
|
single actor's stack frame today), so per [[0001-actor-split]]'s
|
|
resolved decision, alert evaluation moves with metric computation.
|
|
Depends on [[104-move-metric-computation-to-profile-actor]] landing
|
|
first — alert evaluation reads the metric buckets that ticket moves
|
|
onto `ProfileSnapshot`.
|
|
|
|
## Where
|
|
|
|
- `src/engine/handle.rs:141` — `let mut alert_states:
|
|
HashMap<String, AlertRuntimeState> = HashMap::new();`, currently a
|
|
local in the single actor's `run()` loop.
|
|
- `src/engine/handle.rs`'s `Ingest` arm — today, right after
|
|
`write.mutate(...)` publishes, the actor does `write.peek()` and
|
|
calls `alerts::evaluate_alerts(&rules, &mut alert_states, &guard,
|
|
sequence, timestamp, &text)`, then spawns
|
|
`alerts::deliver_for_rule` for each fired alert. This whole block
|
|
moves to wherever the profile actor's per-line processing lives
|
|
(the watch-wake loop from ticket 103/104).
|
|
- `src/engine/alerts.rs:139` (`AlertRuntimeState`), `:241`
|
|
(`evaluate_alerts`), `:384` (`deliver_for_rule`) — signatures take
|
|
the rules list and a `&EngineSnapshot`-shaped guard; check whether
|
|
`evaluate_alerts` needs to change its snapshot parameter type to
|
|
`ProfileSnapshot` (likely yes, since it reads bucket data that now
|
|
lives there) or whether it can stay snapshot-shape-agnostic via
|
|
whatever trait/accessor bucket-reading already uses.
|
|
- Settings access for alert rules/ntfy config
|
|
(`src/engine/handle.rs:170-177`, `settings.lock().unwrap()`) — this
|
|
stays available to the profile actor the same way (it's a
|
|
`SettingsHandle` clone, not primary-actor state), so no primary
|
|
dependency here.
|
|
|
|
## Scope
|
|
|
|
- Move `alert_states` ownership to the profile actor's task-local
|
|
state.
|
|
- Move the post-publish `evaluate_alerts` call and `deliver_for_rule`
|
|
spawn to fire from the profile actor's per-line processing, reading
|
|
`ProfileSnapshot` (post-metric-computation) instead of the primary's
|
|
`EngineSnapshot`.
|
|
- Update `evaluate_alerts`'s signature in `src/engine/alerts.rs` if it
|
|
needs to accept a different snapshot type; keep its actual
|
|
evaluation logic unchanged — this ticket is a relocation, not a
|
|
rewrite of alert semantics.
|
|
- Out of scope: any change to alert rule semantics, ntfy delivery, or
|
|
the `AlertRuntimeState` model itself — purely which actor owns and
|
|
drives evaluation.
|