package delivery import ( "context" "time" ) // Sink — one channel's transport. the impure seam: voice (local audio via the // tts module), ntfy (push), telegram (push through your relay). each is a // separate module with its own unit; the dispatcher holds one per channel. // // A nil Sink = that channel is not wired (the daemon doesn't have to wire all // three at scaffold time). sends to an unwired channel are skipped — the // daemon should wire what the routing table can produce for its configured // severities, but a missing sink is a config gap, not a panic. type Sink interface { Send(ctx context.Context, s Sendable) error } // AckTracker — for sev4 telegram repeat-til-ack. maps a send key (rule name) // → acked + last-sent. the daemon's tick loop calls ShouldRepeat each cycle; // when the user acknowledges (a tap on the telegram message, a voice "got it"), // MarkAcked stops the repeat for that key. // // Pure decision lives in ShouldRepeat (ack.go); this is the stateful seam. // the production impl is a store-backed table; the scaffold's fake is in-memory. type AckTracker interface { WasAcked(ctx context.Context, key string) (bool, error) MarkSent(ctx context.Context, key string, ts time.Time) error LastSent(ctx context.Context, key string) (time.Time, error) MarkAcked(ctx context.Context, key string) error }