# `GEPA.Proposer.Reflective`
[🔗](https://github.com/nshkrdotcom/gepa_ex/blob/v0.3.0/lib/gepa/proposer/reflective.ex#L1)

Reflective mutation proposer.

Generates new candidates through reflection on execution traces.
Candidate text must be proposed by the adapter, a custom proposer, or an
`instruction_proposal` backed by a reflection LLM.

## With LLM-based Instruction Proposal

    llm = GEPA.LLM.req_llm(:openai)
    instruction_proposal = GEPA.Proposer.InstructionProposal.new(llm: llm)

    proposer = Reflective.new(
      adapter: my_adapter,
      trainset: trainset,
      instruction_proposal: instruction_proposal
    )

## Without LLM

If the adapter does not implement `propose_new_texts/4` or `/3`, pass a
`custom_candidate_proposer` function. GEPA no longer ships a production
placeholder mutation path.

# `t`

```elixir
@type t() :: %GEPA.Proposer.Reflective{
  adapter: term(),
  batch_sampler: module() | struct(),
  callbacks: [term()] | nil,
  candidate_selector: module() | struct(),
  custom_candidate_proposer: function() | nil,
  instruction_proposal: GEPA.Proposer.InstructionProposal.t() | nil,
  minibatch_size: pos_integer(),
  module_selector: module() | struct(),
  perfect_score: float(),
  skip_perfect_score: boolean(),
  trainset: GEPA.DataLoader.t()
}
```

# `new`

```elixir
@spec new(keyword()) :: t()
```

# `propose`

```elixir
@spec propose(t(), GEPA.State.t()) ::
  {:ok, GEPA.CandidateProposal.t(), t(), GEPA.State.t()}
  | {:error, term(), t(), GEPA.State.t()}
  | {:none, t(), GEPA.State.t(), map()}
```

Propose a new candidate through reflective mutation.

Algorithm:
1. Select candidate from Pareto front
2. Sample minibatch from training set
3. Evaluate with trace capture
4. Check for perfect scores (optional skip)
5. Generate improved version:
   - Adapter `propose_new_texts`
   - Custom proposer
   - LLM-backed `instruction_proposal`
6. Evaluate new candidate
7. Return proposal if improved

---

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