Resolution order
NotificationCompass evaluates a preference in this order:
- Required-context check, which blocks when no context is available.
- Mandatory definition or channel rule.
- Enforced context policy.
- Explicit context preference.
- Explicit global preference.
- Default context policy.
- Definition default for the current context.
- Definition default for the channel.
- Channel-level default inside the definition.
- Opt-in fallback, which disables the notification.
- Global channel default from configuration.
- Package default from configuration.
The first matching rule wins. A required context is a prerequisite rather than a fallback value: when it is missing, the result is disabled with the context_required reason and global preferences are not consulted. A mandatory result is always enabled and is not modifiable.
Context policies come from NotificationContextPreferenceStore. A default policy applies after user preferences, while an enforced policy takes precedence over them. This distinction lets a community, team, organization, or workspace choose between a suggestion and a rule for all its members. The resolved source is context_policy, and its mode is exposed on ResolvedPreference::$mode.
$preference = $user->effectiveNotificationPreference(
'booking.created',
'mail',
new NotificationContext('organization', 42),
);
$preference->enabled;
$preference->reason->value;
$preference->source;
$preference->isModifiable();
reason is a NotificationDecisionReason code that can be stored in logs or asserted in tests. It distinguishes mandatory rules, user preferences, context policies, defaults, and gate failures such as undeclared channels or unauthorized contexts.
Resetting a preference removes the explicit value. It does not force a channel on or off; the resolver then falls back to the next applicable rule.