# DeactivateNotificationChannel

## Permission Scope

channelRegistry

## Overview

deactivateNotificationChannel flips the `enabled` flag on an existing NotificationChannel registry row to `false`, instructing the dispatcher to skip that channel for every event going forward without creating Notification rows for it. This is the kill-switch path — for example, pausing all EMAIL deliveries while a SendGrid incident is investigated, or temporarily disabling IN_APP. The command is idempotent: calling it on an already-disabled channel returns success without mutating state. Notifications already persisted in earlier dispatches are not affected; only newly arriving events skip the channel.

## Business Rules

- `channelId` is required to identify the target row
- A NotificationChannel row must exist for the supplied `channelId`
- The command is idempotent — invoking it on a row whose `enabled` is already `false` is a successful no-op
- Already-persisted Notifications retain their state; deactivation only suppresses future fan-out for the channel
- Disabling IN_APP is permitted but effectively turns off the inbox surface for new events

## Process Flow

```mermaid
flowchart TD
    A[Receive deactivate channel request] --> B{NotificationChannel exists?}
    B -->|No| C[Return CHANNEL_NOT_FOUND]
    B -->|Yes| D{enabled is already false?}
    D -->|Yes| E[Idempotent no-op, return current row]
    D -->|No| F[Set enabled = false]
    F --> G[Return updated row]
```

## External Dependencies

- None (operates on module-owned NotificationChannel registry only)

## Error Scenarios

- **CHANNEL_NOT_FOUND**: No NotificationChannel row matches the supplied channelId

## Test Cases

- flips enabled from true to false on an existing NotificationChannel row
- returns the existing row unchanged when enabled is already false (idempotent)
- returns CHANNEL_NOT_FOUND when the channelId does not exist
- the next dispatch after deactivation skips the channel and creates no Notification row for it
- already-persisted Notifications on prior events are not mutated
- disabling IN_APP is allowed and results in inbox events being skipped while the flag is off
