- RuleType values are now unique model names with an explicit
storage_value mapping (monthly_day/monthly_weekday -> 'monthly',
custom_days -> 'custom'), fixing the enum aliasing collapse where
two members sharing a value collapsed into one
- to_dict serializes the model name so from_dict(to_dict()) is a true
lossless round-trip; storage mapping is the DAO's documented job
- real @dataclass(frozen=True); non-string non-enum rule_type rejected
- every type carries ONLY its shape's fields: weekday_set forbidden on
DAILY/monthly/yearly; day_of_month+nth forbidden on WEEKLY/CUSTOM_DAYS/
YEARLY; month forbidden on monthly rules (the earlier silent-accept of
these was the root cause behind the kwargs1 mystery)
- from_dict validates weekday_set entries BEFORE set() coercion ([0,
False] cannot collapse) and never leaks raw TypeError from unknown-key
sorting
- purity guard resolves the module by find_spec so relative 'from .db'
imports are caught