Files
ottyl/tickets/105-move-alert-evaluation-to-profile-actor.md
T
Hugh KaznowskiandClaude Sonnet 5 2c1aaa6e9c Break ticket 102's actor split into reviewable sub-tickets (103-108)
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
2026-09-07 13:33:01 +01:00

2.5 KiB

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.