# Notification Categorization and Toast Prioritization in openclaw-windows-node

> Discover how openclaw-windows-node categorizes notifications through a five-step pipeline and prioritizes toast alerts using deduplication, idempotency, and sound settings to avoid spam.

- Repository: [openclaw/openclaw-windows-node](https://github.com/openclaw/openclaw-windows-node)
- Tags: internals
- Published: 2026-06-05

---

**OpenClaw processes every notification through a deterministic five-step categorization pipeline in [`NotificationCategorizer.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/NotificationCategorizer.cs), then prioritizes Windows toast display via time-based deduplication, per-device idempotency checks, and user-configurable sound settings in [`ToastService.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/ToastService.cs) to prevent notification spam.**

The openclaw-windows-node repository implements a sophisticated notification management architecture that separates *what* a message represents from *when* it appears to the user. Understanding the interaction between the **notification categorization system** and **toast notification prioritization** logic allows developers to customize alert routing and ensure critical health or system events surface above routine chatter.

## How the Notification Categorization System Works

The categorization engine lives in [`src/OpenClaw.Shared/NotificationCategorizer.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/src/OpenClaw.Shared/NotificationCategorizer.cs). The `Classify()` method evaluates every `OpenClawNotification` through a strict hierarchical pipeline, returning a display title and semantic type tuple.

### Structured Metadata Processing

The pipeline first inspects high-confidence metadata fields. It checks `notification.Intent` (e.g., “health”, “urgent”) against an immutable `IntentMap`, then falls through to `notification.Channel` (e.g., “calendar”, “email”) against `ChannelMap`. These maps are implemented as **FrozenDictionary** instances, providing O(1) case-insensitive lookups with zero allocation per call.

### User-Defined Rules and Safety

If structured metadata fails to match, the system evaluates a list of `UserNotificationRule` objects supplied by the user. Each rule contains a pattern string (plain text or regex), a target category, and an enable flag. To protect against ReDoS attacks, regex patterns are cached in `_regexCache` with a hard **100 ms timeout** per execution.

### Keyword Fallback Mechanism

When no user rules match, the scanner invokes `ClassifyByKeywords()`, a legacy method that searches the concatenated title and message for hardcoded strings such as “blood sugar”, “urgent”, or “stock”. If all steps fail, the method returns the default `"🤖 OpenClaw"` title with type `"info"`.

Developers can force the pipeline to ignore structured metadata by setting `preferStructuredCategories = false`, useful for backward-compatible code paths.

## Toast Notification Prioritization

Display logic resides in [`src/OpenClaw.Tray.WinUI/Services/ToastService.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/src/OpenClaw.Tray.WinUI/Services/ToastService.cs). Before calling `builder.Show()`, the service applies two layers of deduplication and respects user preferences to determine final priority.

### Deduplication Windows

The service maintains a private `Dictionary<string, DateTime>` called `_recentToastKeys`. Each key combines a `toastTag` with a normalized device ID generated by `BuildToastKey()`. If an identical key was shown within the `ToastDedupeWindow` (**30 seconds**), the new toast is suppressed entirely. This prevents rapid-fire spam from noisy sources while allowing legitimate follow-ups after the window expires.

### Device-Level Idempotency

For lifecycle events like pairing, the service uses a `HashSet<string>` named `_shownPairedToasts` to track device IDs. The `HasShownPairedToast()` and `MarkPairedToastShown()` methods guarantee that "Node paired" toasts appear **exactly once per device**, surviving Bluetooth reconnects and application restarts without redundant alerts.

### Sound Configuration and Thread Safety

The service queries `SettingsManager.NotificationSound` to determine audio priority. When set to **"None"**, the toast renders silently; **"Subtle"** plays a system-provided sound; otherwise the default alert plays. All mutable state—including `_recentToastKeys` and `_shownPairedToasts`—is guarded by a private `_gate` lock, ensuring deterministic behavior during multi-threaded bursts of notifications.

## Practical Implementation Examples

### Classifying a Health Alert

```csharp
var categorizer = new NotificationCategorizer();
var notif = new OpenClawNotification
{
    Message = "Your blood sugar is high",
    Intent = null,
    Channel = null
};

(string title, string type) = categorizer.Classify(notif);
// title => "🩸 Blood Sugar Alert"
// type  => "health"

```

### Adding a Custom User Rule

```csharp
var rule = new UserNotificationRule
{
    Enabled = true,
    IsRegex = false,
    Pattern = "invoice",
    Category = "stock"
};
var userRules = new List<UserNotificationRule> { rule };

var notif = new OpenClawNotification { Message = "New invoice received" };
(string title, string type) = categorizer.Classify(notif, userRules);
// title => "📦 Stock Alert"
// type  => "stock"

```

### Showing a Deduplicated Toast

```csharp
var toastService = new ToastService(() => settingsManager);
var builder = new ToastContentBuilder()
    .AddText("Node paired")
    .AddText("Your device is now connected");

// The tag “node-paired” combined with deviceId is deduped for 30 seconds.
toastService.ShowToast(builder, toastTag: "node-paired", deviceId: deviceId);

```

### Routing Toast Activation

```csharp
ToastActivationRouter.Route(
    action: args.Action,
    getArgument: args.GetArgument,
    new ToastActivationActions
    {
        OpenUrl = url => Process.Start(new ProcessStartInfo(url) { UseShellExecute = true }),
        OpenDashboard = () => dashboard.Show(),
        OpenSettings = () => settings.Show(),
        OpenChat = () => chat.Open(),
        OpenActivity = () => activity.Show(),
        CopyPairingCommand = cmd => Clipboard.SetText(cmd)
    });

```

## Summary

- The **notification categorization system** uses a five-stage pipeline (`Intent` → `Channel` → `User Rules` → `Keywords` → `Default`) implemented in [`NotificationCategorizer.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/NotificationCategorizer.cs) to assign semantic types and display titles.
- **FrozenDictionary** instances and cached regexes with 100 ms timeouts ensure O(1) classification performance without memory pressure or ReDoS vulnerabilities.
- **Toast notification prioritization** in [`ToastService.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/ToastService.cs) relies on a 30-second sliding window deduplicator and a per-device paired-toast registry to eliminate redundant alerts.
- Sound configuration is user-customizable via `SettingsManager.NotificationSound`, with all state mutations protected by a thread-safe `_gate` lock.

## Frequently Asked Questions

### How does openclaw-windows-node prevent duplicate toast notifications?

The system employs a 30-second deduplication window tracked in `_recentToastKeys`, where each entry combines a toast tag and device ID. Identical keys within this window are suppressed. Additionally, lifecycle events like "Node paired" use a separate `_shownPairedToasts` HashSet to ensure exactly one display per device regardless of reconnects.

### What data structures optimize notification categorization performance?

The categorizer uses **FrozenDictionary** implementations for `IntentMap`, `ChannelMap`, and `CategoryTypeMap`, providing immutable, case-insensitive O(1) lookups. User-defined regex patterns are compiled and cached with a 100 ms execution timeout to prevent catastrophic backtracking while maintaining throughput.

### Can users override automatic notification categories?

Yes. Developers can pass a `List<UserNotificationRule>` to the `Classify()` method, allowing custom regex or plaintext patterns to match against notification content. Each rule specifies a target category, and the system evaluates these before falling back to built-in keyword scanning.

### How does the toast service handle concurrent notification requests?

All mutable state in [`ToastService.cs`](https://github.com/openclaw/openclaw-windows-node/blob/main/ToastService.cs)—including the deduplication dictionary and paired-device registry—is guarded by a private `_gate` object. This lock ensures thread-safe access when multiple threads simultaneously attempt to show toasts, preventing race conditions in the deduplication logic.