# `Excessibility.TelemetryCapture.Analyzer`
[🔗](https://github.com/lessthanseventy/excessibility/blob/v0.18.1/lib/telemetry_capture/analyzer.ex#L1)

Behaviour for timeline analyzers.

Analyzers detect patterns across complete timelines and return structured findings.
Analyzers declare their enricher dependencies via `requires_enrichers/0`.

## Example

    defmodule MyApp.CustomAnalyzer do
      @behaviour Excessibility.TelemetryCapture.Analyzer

      def name, do: :custom
      def default_enabled?, do: false
      def requires_enrichers, do: [:memory, :duration]
      def depends_on, do: [:memory]  # runs after memory analyzer

      def analyze(timeline, opts) do
        # Access prior analyzer results via opts[:prior_results]
        prior = Keyword.get(opts, :prior_results, %{})
        memory_stats = get_in(prior, [:memory, :stats])

        %{
          findings: [...],
          stats: %{...}
        }
      end
    end

## Callbacks

- `name/0` - Returns atom identifier for this analyzer
- `default_enabled?/0` - Whether analyzer runs by default without explicit flag
- `requires_enrichers/0` - (Optional) List of enricher names this analyzer needs
- `depends_on/0` - (Optional) List of analyzer names that must run first
- `analyze/2` - Takes complete timeline and options, returns analysis results

## Types

Analysis results contain:
- `:findings` - List of issues found (warnings, errors, info)
- `:stats` - Summary statistics for the analysis

# `analysis_result`

```elixir
@type analysis_result() :: %{findings: [finding()], stats: map()}
```

# `finding`

```elixir
@type finding() :: %{
  severity: :info | :warning | :critical,
  message: String.t(),
  events: [integer()],
  metadata: map()
}
```

# `analyze`

```elixir
@callback analyze(timeline :: map(), opts :: keyword()) :: analysis_result()
```

# `default_enabled?`

```elixir
@callback default_enabled?() :: boolean()
```

# `depends_on`
*optional* 

```elixir
@callback depends_on() :: [atom()]
```

# `name`

```elixir
@callback name() :: atom()
```

# `requires_enrichers`
*optional* 

```elixir
@callback requires_enrichers() :: [atom()]
```

# `get_dependencies`

Gets analyzer dependencies for an analyzer module.
Returns empty list if not defined.

# `get_required_enrichers`

Gets required enrichers for an analyzer module.
Returns empty list if not defined.

# `group_by_view`

```elixir
@spec group_by_view([map()]) :: [[map()]]
```

Splits a timeline's events into per-view sublists, preserving order.

A journey test drives several LiveViews, so a raw timeline interleaves
unrelated processes. Analyzers that compare *consecutive* events (memory
growth, render efficiency) must not treat a `UserLoginLive` mount sitting
next to a `MarketplaceLive.Index` render as a transition — that's an
artifact of the interleaving, not the code (issue #142).

Events are grouped by `:view_module`; groups appear in first-seen order
and events keep their relative order within a group. Events without a
`:view_module` (older fixtures, single-view timelines) collapse to one
group, so the ungrouped case is unchanged.

# `sort_by_dependencies`

Topologically sorts analyzers based on their dependencies.
Returns analyzers in execution order (dependencies first).

---

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