The notification control model — what the user wants to see and where, as per-category defaults plus per-strategy overrides. Pure data
- pure resolution; no I/O, so it's trivially testable and a UI/editor can read & write it later.
Shape:
%{
categories: %{
fills: %{enabled: true, min_value: 25.0, channels: [:telegram]},
resolutions: %{enabled: true, channels: [:telegram]},
errors: %{enabled: true, channels: [:telegram, :log]},
daily_summary: %{enabled: true, channels: [:telegram]}
},
# per-strategy overrides: tag => %{category => partial pref}
strategies: %{
"surge" => %{fills: %{enabled: false}},
"ai" => %{fills: %{enabled: true, min_value: 0.0}}
},
default_channels: [:log]
}Resolution merges the category default with any per-strategy override for that (strategy, category). Strategy tags are arbitrary strings, so user-written strategies configure exactly like built-ins.
Summary
Functions
Should category (for optional strategy, at optional value) be delivered?
Channel names for category (for optional strategy).
The default control set: every category on, routed to default_channels, no overrides.
Seed controls from a strategy catalog (list of tags or maps with
a :tag), so every strategy — built-in or user-written — is present
and individually configurable (UI/editor can flip each). Overrides
start empty (inherit the category default).
Types
Functions
Should category (for optional strategy, at optional value) be delivered?
Channel names for category (for optional strategy).
@spec default() :: t()
The default control set: every category on, routed to default_channels, no overrides.
Seed controls from a strategy catalog (list of tags or maps with
a :tag), so every strategy — built-in or user-written — is present
and individually configurable (UI/editor can flip each). Overrides
start empty (inherit the category default).