# ADR-0003: Parse grammar — verbatim model ids, inline alias expansion, chains **Status:** Accepted — 2026-06-10 ## Context Callers (mort first) address models by string: single targets, tier aliases, and comma-separated failover chains, with custom and env-defined providers as first-class elements. go-llm's grammar is close but nests alias-chains as composite Models and strips `:low/:medium/:high` reasoning suffixes, which collides with Ollama-style tags (`minimax-m3:cloud`) and Google-style ids. ## Decision Grammar (binding, from the kickoff): ``` spec := element ("," element)* element := target | alias target := provider "/" model # model = everything after the FIRST "/", # up to the next comma, passed VERBATIM alias := bare token, no slash ``` - Provider resolution order per target: registered providers (built-ins, RegisterProvider, eagerly env-loaded) → lazy `LLM_{UPPER(name)}` env DSN (ADR-0004) → error naming both places checked. - Aliases expand **inline** wherever they appear (head/middle/tail), recursively, into the flat element list. Cycles are detected via the expansion stack and return `ErrAliasCycle` — never a hang. Inline (not nested-Model, as in go-llm) expansion keeps one flat chain so health skipping and error reporting see every element uniformly. - Duplicate elements after expansion are dropped (first occurrence wins): retrying an already-failed target in the same pass is never useful. - A single element and a multi-element chain return the same `Model` (a chain of one) — identical retry/health semantics, callers never branch. - **No reasoning-suffix stripping.** mort's `:high` dialect is handled by mort's spec layer during migration; majordomo will expose reasoning effort as an explicit request option instead. - The package-level `Default()` registry (lazy, loads process env) backs `majordomo.Parse` for go-llm-style one-call ergonomics; `New()` builds isolated registries for tests/multi-tenant use. ## Consequences - `m1/richardyoung/qwen3-14b-abliterated:q4_K_M` (a real mort tier value) parses as provider `m1`, model `richardyoung/qwen3-14b-abliterated:q4_K_M`. - A bare token that is a provider name yields a targeted error ("use openai/"). - Alias updates after Parse don't affect already-built Models (expansion is at Parse time). mort re-parses per request, so DB-tier edits still apply. ## Alternatives considered - Nested alias expansion (go-llm): opaque chains inside chains; health skipping can't see the elements. Rejected. - Reasoning suffixes in the grammar: breaks verbatim ids. Rejected.