Skip to content

API reference

Everything below is importable from poincare_orchestra unless noted. Public API changes are additive and listed in the changelog.

Scenario model

scenario

Scenario model: a use case is data, not code.

A scenario bundles the domain hierarchy and dated facts that seed the memory, the specialist roles and their capability profiles, example tasks with the facets a good answer must cover, and the controller policy. Everything the orchestrator does is driven by it, so a new use case is a new scenario file (JSON or YAML; see :meth:Scenario.from_file).

OBJECTIVES module-attribute

OBJECTIVES: tuple[str, ...] = ('coverage', 'density', 'agreement', 'recency', 'cost')

The five synthesis objectives a policy may weight or constrain.

DECAY_KINDS module-attribute

DECAY_KINDS: tuple[str, ...] = ('none', 'half_life', 'ebbinghaus', 'validity')

Accepted kinds in a ControllerPolicy.decay spec.

Scenario

Bases: BaseModel

A complete use case: memory contents, roles, capability profiles, tasks and policy.

Validation runs on construction and on :meth:from_file: duplicate ids, parents declared after their children, roles that reference unknown profiles and tasks without facets are reported with their position.

name class-attribute instance-attribute

name: str = Field(min_length=1)

title instance-attribute

title: str

description instance-attribute

description: str

knowledge instance-attribute

knowledge: list[KnowledgeItem]

profiles instance-attribute

profiles: list[CapabilityProfile]

roles instance-attribute

roles: list[AgentRole]

tasks instance-attribute

tasks: list[Task]

policy class-attribute instance-attribute

policy: ControllerPolicy = Field(default_factory=ControllerPolicy)

decay_policy

decay_policy() -> DecayPolicy

The poincare_memory decay policy described by policy.decay.

build_memory

build_memory(now: datetime | None = None, clock: Callable[[], datetime] | None = None, **kwargs: Any) -> Memory

Seed a fresh :class:poincare_memory.Memory with the knowledge items.

now anchors the relative dates (default: the current UTC time); clock overrides the memory's clock; other keyword arguments go to Memory (for example embedder=).

role

role(name: str) -> AgentRole

profile

profile(name: str) -> CapabilityProfile

to_dict

to_dict() -> dict[str, Any]

JSON-compatible dict with defaults and None fields omitted (the on-disk form).

to_file

to_file(path: str | Path, format: FileFormat | None = None) -> Path

Write the scenario as JSON or YAML; the format follows the suffix unless given.

YAML needs PyYAML (pip install 'poincare-orchestra[yaml]').

from_file classmethod

from_file(path: str | Path, format: FileFormat | None = None) -> Scenario

Load and validate a scenario from a JSON or YAML file.

Raises:

Type Description
FileNotFoundError

if path does not exist.

ValueError

if the suffix is not .json/.yaml/.yml and no format is given, or if the file is not a mapping.

ValidationError

if the contents are inconsistent (see the class docstring).

ImportError

for YAML without PyYAML installed.

KnowledgeItem

Bases: BaseModel

One record of the scenario memory.

parent builds the hierarchy (parents must be listed before their children); days_ago dates a record relative to the run; the two valid_* fields give it a validity window for the validity decay kind.

id class-attribute instance-attribute

id: str = Field(min_length=1)

text class-attribute instance-attribute

text: str = Field(min_length=1)

parent class-attribute instance-attribute

parent: str | None = None

days_ago class-attribute instance-attribute

days_ago: float | None = None

Age of a dated record; undated items are structural knowledge and count as current.

meta class-attribute instance-attribute

meta: dict[str, Any] = Field(default_factory=dict)

valid_from_days_ago class-attribute instance-attribute

valid_from_days_ago: float | None = None

valid_until_days_ago class-attribute instance-attribute

valid_until_days_ago: float | None = None

Task

Bases: BaseModel

An example question with the facets (substrings) a good answer must contain.

question class-attribute instance-attribute

question: str = Field(min_length=1)

expected_facets class-attribute instance-attribute

expected_facets: list[str] = Field(default_factory=list, description='substrings a good answer must contain')

notes class-attribute instance-attribute

notes: str = ''

ControllerPolicy

Bases: BaseModel

Budget, retrieval, objective weights, epsilon constraints and temporal policy of a scenario.

max_subtasks class-attribute instance-attribute

max_subtasks: int = Field(default=4, ge=1)

total_budget class-attribute instance-attribute

total_budget: float = Field(default=6.0, gt=0)

min_budget class-attribute instance-attribute

min_budget: float = Field(default=0.5, ge=0)

retrieval_k class-attribute instance-attribute

retrieval_k: int = Field(default=5, ge=1)

objectives class-attribute instance-attribute

objectives: dict[str, float] = Field(default_factory=lambda: {'coverage': 0.4, 'density': 0.2, 'agreement': 0.1, 'recency': 0.15, 'cost': 0.15})

Weights w_i of eq. 7; keys must be objective names.

constraints class-attribute instance-attribute

constraints: dict[str, float] = Field(default_factory=lambda: {'coverage': 0.5, 'density': 0.2})

Epsilon thresholds ε_j of eq. 7 in [0, 1]; keys must be objective names.

refine_rounds class-attribute instance-attribute

refine_rounds: int = Field(default=1, ge=0)

decay class-attribute instance-attribute

decay: str = 'half_life:720'

none | half_life:<hours> | ebbinghaus:<hours> | validity | half_life:<h>+validity

parse_decay

parse_decay(spec: str) -> DecayPolicy

Build a poincare_memory decay policy from a spec string.

Grammar: none | half_life:<hours> | ebbinghaus:<hours> | validity, and any of these joined with + for a product (for example half_life:2160+validity). Hours default to 720 (half-life) and 168 (Ebbinghaus stability) when omitted.

Raises:

Type Description
ValueError

if a kind is unknown or an hour value is not a positive number.

load_scenario

load_scenario(name: str | Path) -> Scenario

A bundled scenario by name, or any scenario file by path (.json, .yaml, .yml).

Raises:

Type Description
KeyError

if name is neither a bundled scenario nor an existing file.

FileNotFoundError

if name looks like a scenario file but does not exist.

list_scenarios

list_scenarios() -> list[str]

Names of the bundled scenarios.

Roles and capability profiles

profiles

Capability profiles and agent roles (paper §IV-B capability matching, §IV-D capability profiling).

CapabilityProfile

Bases: BaseModel

Static description of what a model/provider is good at and what it costs.

name instance-attribute

name: str

provider instance-attribute

provider: str

domains class-attribute instance-attribute

domains: list[str] = Field(default_factory=list)

reasoning class-attribute instance-attribute

reasoning: float = 0.5

creativity class-attribute instance-attribute

creativity: float = 0.5

reliability class-attribute instance-attribute

reliability: float = 0.5

cost_per_call class-attribute instance-attribute

cost_per_call: float = 1.0

latency_s class-attribute instance-attribute

latency_s: float = 1.0

fit

fit(domains: list[str], intent: str) -> float

Capability-matching score in [0, 1] for a sub-task with these domains and intent.

AgentRole

Bases: BaseModel

name instance-attribute

name: str

description instance-attribute

description: str

system_prompt instance-attribute

system_prompt: str

domains class-attribute instance-attribute

domains: list[str] = Field(default_factory=list)

profile instance-attribute

profile: str

Schemas

schema

Standardised schemas shared by every component (paper §IV-D "response standardization").

SubTask

Bases: BaseModel

One unit of the decomposition (eq. 5) with its role, dependencies, gain, cost and budget.

id instance-attribute

id: str

question instance-attribute

question: str

role class-attribute instance-attribute

role: str | None = None

depends_on class-attribute instance-attribute

depends_on: list[str] = Field(default_factory=list)

domains class-attribute instance-attribute

domains: list[str] = Field(default_factory=list)

branch_id class-attribute instance-attribute

branch_id: str | None = None

entity_ids class-attribute instance-attribute

entity_ids: list[str] = Field(default_factory=list)

query_text class-attribute instance-attribute

query_text: str | None = None

Text used for retrieval; the question itself may carry a display-only focus label.

expected_gain class-attribute instance-attribute

expected_gain: float = 0.0

cost class-attribute instance-attribute

cost: float = 1.0

budget class-attribute instance-attribute

budget: float = 0.0

skipped class-attribute instance-attribute

skipped: bool = False

Plan

Bases: BaseModel

Output of the controller: query analysis plus the ordered sub-tasks.

query instance-attribute

query: str

intent instance-attribute

intent: str

complexity instance-attribute

complexity: float

domains class-attribute instance-attribute

domains: list[str] = Field(default_factory=list)

entities class-attribute instance-attribute

entities: list[str] = Field(default_factory=list)

subtasks class-attribute instance-attribute

subtasks: list[SubTask] = Field(default_factory=list)

Claim

Bases: BaseModel

An atomic statement with confidence and the memory record ids that support it.

text instance-attribute

text: str

confidence class-attribute instance-attribute

confidence: float = 0.5

sources class-attribute instance-attribute

sources: list[str] = Field(default_factory=list)

CompletionRequest

Bases: BaseModel

What a provider receives: the sub-task, the role's system prompt and the retrieved context.

subtask instance-attribute

subtask: SubTask

role_name instance-attribute

role_name: str

system_prompt instance-attribute

system_prompt: str

context class-attribute instance-attribute

context: list[str] = Field(default_factory=list)

context_ids class-attribute instance-attribute

context_ids: list[str] = Field(default_factory=list)

context_weights class-attribute instance-attribute

context_weights: list[float] = Field(default_factory=list)

budget class-attribute instance-attribute

budget: float = 1.0

AgentResponse

Bases: BaseModel

Standardised provider output (paper §IV-D): content, claims, confidence, cost signals, refusal flag.

subtask_id instance-attribute

subtask_id: str

role instance-attribute

role: str

provider instance-attribute

provider: str

content instance-attribute

content: str

claims class-attribute instance-attribute

claims: list[Claim] = Field(default_factory=list)

confidence class-attribute instance-attribute

confidence: float = 0.5

sources class-attribute instance-attribute

sources: list[str] = Field(default_factory=list)

latency_s class-attribute instance-attribute

latency_s: float = 0.0

tokens class-attribute instance-attribute

tokens: int = 0

refused class-attribute instance-attribute

refused: bool = False

meta class-attribute instance-attribute

meta: dict[str, Any] = Field(default_factory=dict)

ThrottleEvent

Bases: BaseModel

A response that broke its provider's baseline, or a provider exception (reason error: <type>).

provider instance-attribute

provider: str

reasons instance-attribute

reasons: list[str]

latency_s instance-attribute

latency_s: float

tokens instance-attribute

tokens: int

density instance-attribute

density: float

SynthesisResult

Bases: BaseModel

Fused answer with the five objective scores, eq. 7 weighted sum, constraint status and uncertainty.

answer instance-attribute

answer: str

claims instance-attribute

claims: list[Claim]

objective_scores instance-attribute

objective_scores: dict[str, float]

weighted_score instance-attribute

weighted_score: float

constraints_met instance-attribute

constraints_met: bool

violated class-attribute instance-attribute

violated: list[str] = Field(default_factory=list)

uncertainty instance-attribute

uncertainty: float

refinements class-attribute instance-attribute

refinements: int = 0

RunResult

Bases: BaseModel

Everything one :meth:~poincare_orchestra.Orchestrator.run produced.

question instance-attribute

question: str

plan instance-attribute

plan: Plan

responses instance-attribute

responses: list[AgentResponse]

synthesis instance-attribute

synthesis: SynthesisResult

allocations instance-attribute

allocations: dict[str, float]

throttle_events class-attribute instance-attribute

throttle_events: list[ThrottleEvent] = Field(default_factory=list)

trace class-attribute instance-attribute

trace: list[str] = Field(default_factory=list)

to_json

to_json(indent: int | None = 2) -> str

The whole result (plan, responses, synthesis, allocations, events, trace) as JSON text.

from_json classmethod

from_json(text: str) -> RunResult

Inverse of :meth:to_json.

explain

explain() -> str

Human-readable trace: question, plan, responses, throttle events, scores and the answer.

Controller

controller

Metacognitive controller (paper §III-B, §III-C, §IV-B).

Four executive functions, each a small, inspectable step: 1. query analysis and intent recognition, with the memory hierarchy as the domain model; 2. hierarchical decomposition into coordinated sub-tasks Q -> {q_1..q_n} (eq. 5); 3. capability matching of sub-tasks to roles and profiles; 4. resource allocation by expected information gain per cost, I*(q_i) = argmax_I G(q_i, I) / C(I) (eq. 6), run as an investment cycle: invest, measure yield, re-invest the remainder.

The implementation is rule-based and offline so the control loop is transparent; an LLM-backed planner can replace decompose without touching the rest.

EPHEMERAL_KINDS module-attribute

EPHEMERAL_KINDS = {'event', 'ticket', 'issue', 'case', 'observation'}

InvestmentScheduler

InvestmentScheduler(policy: ControllerPolicy, embedder: HashingEmbedder | None = None)

Allocate compute by expected information gain per cost (eq. 6), then re-invest after measuring yield.

policy instance-attribute

policy = policy

embedder instance-attribute

embedder = embedder or HashingEmbedder(dim=128)

expected_gain

expected_gain(plan: Plan, memory: Memory) -> dict[str, float]

Gain = relevance of what the memory holds for the sub-task × novelty versus the other sub-tasks.

allocate

allocate(plan: Plan, memory: Memory) -> dict[str, float]

Keep sub-tasks in order of gain/cost while they fit the budget, then split the budget by gain/cost.

Sub-tasks that do not fit are marked skipped with budget 0: the allocation decision of eq. 6 made explicit instead of overspending.

measure_yield staticmethod

measure_yield(response: AgentResponse) -> float

Information yield proxy: distinct claims per 100 tokens (the paper's 'information density').

reinvest

reinvest(plan: Plan, responses: list[AgentResponse], spent: float) -> dict[str, float]

Extra budget for the sub-tasks with the lowest yield (refinement targets).

QueryAnalysis dataclass

QueryAnalysis(query: str, intent: str, complexity: float, domains: list[str], branch_ids: list[str], entity_ids: list[str], retrieved_ids: list[str], clauses: list[str])

Result of :func:analyze: intent, complexity, the memory branches and entities a query touches.

query instance-attribute

query: str

intent instance-attribute

intent: str

complexity instance-attribute

complexity: float

domains instance-attribute

domains: list[str]

branch_ids instance-attribute

branch_ids: list[str]

entity_ids instance-attribute

entity_ids: list[str]

retrieved_ids instance-attribute

retrieved_ids: list[str]

clauses instance-attribute

clauses: list[str]

analyze

analyze(query: str, memory: Memory, k: int = 5) -> QueryAnalysis

Intent, complexity, domain branches and named entities for a query.

decompose

decompose(analysis: QueryAnalysis, memory: Memory, roles: list[AgentRole], profiles: list[CapabilityProfile], policy: ControllerPolicy) -> Plan

Split a query into facet sub-tasks (one per domain branch) and sequential clause sub-tasks, then match roles.

entity_hits

entity_hits(query: str, memory: Memory, max_prefix: int = 24) -> list[str]

Records whose leading phrase (the text before ':' or '(') names an entity mentioned in the query.

Convention: entity records read "Athlete A: ...", "Ticket 4411: ...", "Release 2.4 (...)".

branch_of

branch_of(memory: Memory, id_: str) -> str

The ancestor one level below the root (the domain branch), or the node itself.

Ensemble

ensemble

Provider-agnostic ensemble (paper §IV-D): providers, throttling detection, load balancing, failover.

Provider

Bases: Protocol

Anything with a name and a complete(request) -> AgentResponse method.

name instance-attribute

name: str

complete

complete(request: CompletionRequest) -> AgentResponse

MockProvider

MockProvider(name: str, degrade_after: int | None = None, latency_s: float = 0.01, seed: int = 0, fail_after: int | None = None)

Deterministic provider that answers from the retrieved context.

It lets the whole orchestration run offline and in tests. degrade_after simulates a throttled commercial model: after that many calls, answers become truncated, slow and sometimes refused, which the detector should catch and the router should route around. fail_after simulates an outage: after that many calls every call raises RuntimeError.

name instance-attribute

name = name

degrade_after instance-attribute

degrade_after = degrade_after

fail_after instance-attribute

fail_after = fail_after

latency_s instance-attribute

latency_s = latency_s

calls instance-attribute

calls = 0

rng instance-attribute

rng = np.random.default_rng(seed)

complete

complete(request: CompletionRequest) -> AgentResponse

LiteLLMProvider

LiteLLMProvider(name: str, model: str, temperature: float = 0.2, max_tokens: int = 600, timeout_s: float | None = None, **kwargs: Any)

Any model LiteLLM can reach (pip install 'poincare-orchestra[litellm]').

model is a LiteLLM model string such as openai/gpt-4o-mini, anthropic/claude-haiku-5-5 or ollama/llama3; credentials come from the environment variables LiteLLM reads for that provider. The prompt asks for a JSON object {"answer": str, "claims": [{"text", "confidence", "sources"}]}; plain text is accepted as a fallback. timeout_s is passed to litellm.completion(timeout=...); a timeout raises and the router treats it like any other provider error (refused response, throttle event, failover). Extra keyword arguments go to litellm.completion unchanged.

name instance-attribute

name = name

model instance-attribute

model = model

temperature instance-attribute

temperature = temperature

max_tokens instance-attribute

max_tokens = max_tokens

timeout_s instance-attribute

timeout_s = timeout_s

kwargs instance-attribute

kwargs = kwargs

build_messages

build_messages(request: CompletionRequest) -> list[dict[str, str]]

The system and user messages sent for a request.

complete

complete(request: CompletionRequest) -> AgentResponse

ThrottleDetector

ThrottleDetector(window: int = 6, min_history: int = 2, latency_factor: float = 3.0, length_ratio: float = 0.4, density_drop: float = 0.35)

Flags responses whose latency, length, density or refusal pattern breaks the provider's baseline.

The baseline is the provider's own recent responses (the last window that were not flagged); while a provider has fewer than min_history of them, the pooled responses of all providers stand in for it. A response is flagged when it is refused, when its latency exceeds latency_factor times the baseline median, when its token count is below length_ratio times the baseline median, or when its information density is more than density_drop below the baseline median. Flagged responses do not enter the baseline.

window instance-attribute

window = window

min_history instance-attribute

min_history = min_history

latency_factor instance-attribute

latency_factor = latency_factor

length_ratio instance-attribute

length_ratio = length_ratio

density_drop instance-attribute

density_drop = density_drop

history instance-attribute

history: dict[str, deque[tuple[float, int, float]]] = defaultdict(lambda: deque(maxlen=window))

observe

observe(response: AgentResponse) -> ThrottleEvent | None

Record a response; return a :class:ThrottleEvent if it breaks the baseline, else None.

Router

Router(providers: list[Provider], profiles: list[CapabilityProfile], detector: ThrottleDetector | None = None, cooldown_calls: int = 3)

Chooses a provider for each role by capability profile and health; fails over on throttling.

Candidates are ordered by: the profile's preferred provider first, then providers that are not cooling down, then the least loaded. A flagged response or a provider exception puts the provider in cooldown: it is skipped for the next cooldown_calls calls and the next candidate is tried; the last response is returned if every candidate fails. When every provider is cooling down they are tried anyway, least-degraded first, so a call never fails only because of cooldowns. call never raises on a provider exception: it is converted into a refused :class:AgentResponse and a :class:ThrottleEvent with reason error: <exception type>.

providers instance-attribute

providers = {p.name: p for p in providers}

profiles instance-attribute

profiles = {p.name: p for p in profiles}

detector instance-attribute

detector = detector or ThrottleDetector()

cooldown_calls instance-attribute

cooldown_calls = cooldown_calls

degraded instance-attribute

degraded: dict[str, int] = {}

load instance-attribute

load: dict[str, int] = defaultdict(int)

events instance-attribute

events: list[ThrottleEvent] = []

tick

tick() -> None

Advance every cooldown by one call (called automatically at the end of each :meth:call).

call

call(request: CompletionRequest, profile_name: str) -> AgentResponse

Complete request with the best healthy provider for profile_name (see the class docstring).

parse_completion

parse_completion(text: str) -> tuple[str, list[Claim]]

Split a model reply into (answer, claims).

The reply is expected to contain a JSON object {"answer": str, "claims": [...]} (code fences and surrounding prose are tolerated); otherwise the whole text is the answer and there are no claims. Malformed claim entries are skipped.

looks_like_refusal

looks_like_refusal(text: str) -> bool

Short replies of the form "I cannot/can't/am unable to help|assist|comply".

information_density

information_density(text: str) -> float

Unique-token ratio: a cheap proxy for the paper's bits-per-token notion.

Synthesis

synthesis

Response synthesis (paper §III-D, §IV-E): fusion, multi-objective scoring with epsilon constraints, uncertainty from agreement, and iterative refinement.

Objectives dataclass

Objectives(coverage: float, density: float, agreement: float, recency: float, cost: float)

coverage instance-attribute

coverage: float

density instance-attribute

density: float

agreement instance-attribute

agreement: float

recency instance-attribute

recency: float

cost instance-attribute

cost: float

as_dict

as_dict() -> dict[str, float]

synthesize

synthesize(plan: Plan, responses: list[AgentResponse], recency_weights: dict[str, float], weights: dict[str, float], constraints: dict[str, float], spent: float, budget: float, refinements: int = 0) -> SynthesisResult

fuse_claims

fuse_claims(responses: list[AgentResponse], merge_threshold: float = 0.85) -> list[Claim]

Merge near-duplicate claims across sources; confidence is the max, sources are pooled.

score_objectives

score_objectives(plan: Plan, responses: list[AgentResponse], claims: list[Claim], recency_weights: dict[str, float], spent: float, budget: float) -> Objectives

weighted_sum

weighted_sum(obj: dict[str, float], weights: dict[str, float], constraints: dict[str, float]) -> tuple[float, bool, list[str]]

Paper eq. 7: maximise Σ w_i f_i subject to f_j ≥ ε_j.

agreement

agreement(responses: list[AgentResponse]) -> float

Mean pairwise overlap of claim sources across distinct providers (1 = every provider agrees).

compose_answer

compose_answer(plan: Plan, claims: list[Claim], responses: list[AgentResponse]) -> str

Orchestrator

orchestrator

The end-to-end loop: analyse → decompose → allocate → execute → synthesise → refine.

logger module-attribute

logger = logging.getLogger('poincare_orchestra')

EvalReport

Bases: BaseModel

Facet recall and synthesis scores over a scenario's bundled tasks (see :meth:Orchestrator.evaluate).

scenario instance-attribute

scenario: str

tasks instance-attribute

tasks: int

facet_recall instance-attribute

facet_recall: float

mean_weighted_score instance-attribute

mean_weighted_score: float

constraints_met_rate instance-attribute

constraints_met_rate: float

throttle_events instance-attribute

throttle_events: int

per_task class-attribute instance-attribute

per_task: list[dict[str, Any]] = Field(default_factory=list)

summary

summary() -> str

to_json

to_json(indent: int | None = 2) -> str

The report as JSON text.

Orchestrator

Orchestrator(scenario: Scenario, providers: list[Provider] | None = None, policy: ControllerPolicy | None = None, memory: Memory | None = None, clock: Callable[[], datetime] | None = None, detector: ThrottleDetector | None = None)

Runs questions through a scenario: the eight steps of the paper's control loop.

With providers=None one offline :class:MockProvider is created per provider name used by the scenario's capability profiles. Pass :class:LiteLLMProvider instances (or any object satisfying :class:Provider) for real models; their names should match the provider field of the profiles so that capability matching routes to them.

scenario instance-attribute

scenario = scenario

policy instance-attribute

policy = policy or scenario.policy

memory instance-attribute

memory = memory or scenario.build_memory(clock=clock)

router instance-attribute

router = Router(providers, scenario.profiles, detector=detector)

scheduler instance-attribute

scheduler = InvestmentScheduler(self.policy)

run

run(question: str) -> RunResult

Answer one question; never raises on provider failures (they become refused responses and events).

evaluate

evaluate() -> EvalReport

Run every bundled task and measure facet recall (expected substrings found in the answer and responses).

Command line

cli

poincare-orchestra list | run | eval | inspect | export | validate.

Every command that takes a scenario accepts a bundled name (list prints them) or the path to a .json / .yaml scenario file. Exit codes: 0 on success, 1 when validate finds a problem, 2 when a scenario cannot be loaded.

main

main(argv: list[str] | None = None) -> int

build_parser

build_parser() -> ArgumentParser

configure_logging

configure_logging(verbosity: int) -> None

Send the poincare_orchestra logger to stderr: warnings by default, -v INFO, -vv DEBUG.

Bundled scenarios

poincare_orchestra.scenarios.sprint_coaching, poincare_orchestra.scenarios.clinical_anatomy and poincare_orchestra.scenarios.support_kb each expose a SCENARIO constant (a Scenario) built from module-level KNOWLEDGE, PROFILES, ROLES and TASKS lists. load_scenario(name) returns that constant; the same scenarios are exported as YAML under scenarios/ in the repository.