defmodule CouncilEx.AutoCouncil.Strategies.LLMBuild do
@moduledoc """
*Stub.* Synthesizer strategy: an LLM designs a fresh `DynamicCouncil` for
the prompt — picking members, system prompts, rounds, and a chair — then
the resolver runs that synthesized council.
## Planned interface
AutoCouncil.new(
strategy: :llm_build,
options: [
profile: "openai_balanced", # mid-tier model recommended
member_palette: [...], # optional archetype hints
max_members: 5,
allowed_rounds: [:independent_analysis, :peer_critique, :consensus_vote]
]
)
Returns a `{:built, %DynamicCouncil{}}` decision so callers can tell from
the meta that the council was synthesized fresh (non-deterministic) rather
than chosen from a known catalog.
## Hard constraints to enforce
* 2..N members (N from `:max_members`).
* Rounds restricted to `:allowed_rounds`.
* `DynamicCouncil.validate/1` must pass before returning.
* Hard timeout on the planner call to avoid runaway latency.
## Open design questions (deferred)
* Schema enforcement: structured output vs prompt + post-validate.
* Telemetry on synthesized plans (members chosen, member-prompt length).
* Whether to store synthesized plans in the registry for reuse (would
cross from `:built` into `:dynamic` on subsequent hits — opt-in).
## Cache notes
Caching `:built` results is *opt-in only*. Caching turns a non-deterministic
feature into a deterministic one, which can surprise callers who expected
fresh plans. If added, key on prompt embedding + catalog version + planner
model id, and document the change in semantics loudly.
Calling `resolve/2` today returns `{:error, :not_implemented}`.
"""
@behaviour CouncilEx.AutoCouncil.Strategy
@impl true
def resolve(_prompt, _auto), do: {:error, :not_implemented}
end