# `Chimeway.Traces`
[🔗](https://github.com/jonlunsford/chimeway/blob/v1.0.0/lib/chimeway/traces.ex#L1)

Public query context for operator trace surfaces.

Provides four query shapes over the durable lifecycle chain
(event → notification → delivery → attempt):

## Functions

- `get_trace/1` — load full trace for a single event by event_id
- `find_traces_for_recipient/2` — load recent traces for a recipient
- `find_traces_by_correlation_id/1` — load events by correlation_id string
- `explain_delivery/1` — structured explanation for a single delivery
- `aggregate_outcomes/1` — grouped lifecycle counts by notification key, channel, and outcome

## Usage in IEx

    # Full trace for one event
    {:ok, event} = Chimeway.Traces.get_trace("event-uuid-here")
    event.notifications |> Enum.flat_map(& &1.deliveries)

    # Why was this delivery suppressed?
    {:ok, explanation} = Chimeway.Traces.explain_delivery("delivery-uuid-here")
    explanation.suppression_reason  #=> "channel_disabled"
    explanation.timeline            #=> [%{at: ~U[...], event: :event_created, detail: %{}}, ...]

    # All traces for a recipient
    traces = Chimeway.Traces.find_traces_for_recipient("user:123", notification_key: "order_shipped")

# `aggregate_outcomes`

```elixir
@spec aggregate_outcomes(keyword()) :: [map()]
```

Returns grouped lifecycle outcome counts from durable delivery state only.

Supported filters:
- `:notification_key` — restricts results to one stable notification key
- `:channel` — restricts results to one delivery channel
- `:outcomes` — list of explicit lifecycle buckets to keep
- `:inserted_after` / `:inserted_before` — filter by delivery insert timestamps
- `:updated_after` / `:updated_before` — filter by delivery update timestamps

Result rows contain safe identifiers and counts only:

    %{notification_key: "comment.created", channel: "email", outcome: "sent", count: 42}

# `aggregate_outcomes_for_notification`

```elixir
@spec aggregate_outcomes_for_notification(
  String.t(),
  keyword()
) :: [map()]
```

Convenience wrapper for grouped lifecycle outcome counts for one notification key.

# `explain_delivery`

```elixir
@spec explain_delivery(
  String.t(),
  keyword()
) :: {:ok, Chimeway.Traces.Explanation.t()} | {:error, :not_found}
```

Returns a structured explanation for the given delivery_id.

The explanation includes the full timeline derived from row timestamps,
the final status, suppression reason, and last attempt outcome.

Returns `{:ok, %Chimeway.Traces.Explanation{}}` or `{:error, :not_found}`.

# `find_traces_by_correlation_id`

```elixir
@spec find_traces_by_correlation_id(
  String.t(),
  keyword()
) :: [Chimeway.Events.Event.t()]
```

Returns all events with the given correlation_id, each with associations preloaded.

Returns `[]` when no events match — never returns an error tuple since
correlation IDs are user-supplied and may not be unique or present.

# `find_traces_for_recipient`

```elixir
@spec find_traces_for_recipient(
  String.t(),
  keyword()
) :: [Chimeway.Notifications.Notification.t()]
```

Returns recent notification traces for the given recipient.

Options:
- `notification_key:` (string) — filter to a specific notification type
- `limit:` (integer, default 50) — maximum number of notifications returned

Uses explicit joins to avoid N+1 queries.

# `get_trace`

```elixir
@spec get_trace(
  String.t(),
  keyword()
) :: {:ok, Chimeway.Events.Event.t()} | {:error, :not_found}
```

Returns the full event trace for the given event_id with all associations preloaded.

Preloads: `[notifications: [deliveries: :attempts]]`

Returns `{:ok, event}` or `{:error, :not_found}`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
