The chat pipeline
The five layers a chat message passes through, what each one loads, and what the reasoner can fetch for itself.
Chat is the most grounded surface in the app. A message passes through five layers before the model writes anything, and the model can fetch more as it reasons.
The layers exist to put the right context in the prompt before the expensive model runs, so the reasoner spends its tool calls on what pre-loading missed rather than on everything.
The five layers
Only the last layer writes anything. The first four decide what the model gets to see, which is where most of the difference between a good answer and a vague one is settled.
Not every turn runs all of them. A greeting is caught by a pattern before classification and skips the two layers after it. A question the classifier reads as unrelated to your deck skips the deck load, so the model answers without your list even though it is sitting in the database.
A layer that fails does not stop the turn either. Each one catches its own errors and carries on with less context, so a degraded answer is more likely than an error message. That is the right tradeoff for a chat box and it does mean quality varies for reasons the answer itself will not mention.
Prechecks
Credit balance, then a rate limit of 10 messages per minute counted across your conversations.
Classification
A short Haiku call reads the conversation and returns an intent
(recommendations, analysis, rules or chat), a confidence, and flags for
what to pre-load. Greetings skip it entirely on a regex, which also skips the
two layers after it.
Rules are the interesting case. The rules flag is hardcoded to false, so
nothing rules-related is ever pre-loaded. Rules reach the model two other ways:
a compressed rules summary sits in every prompt, and the reasoner has three
tools for fetching specifics. The reasoning is that a query derived at classify
time picks worse passages than the model's own mid-reasoning lookups. That
tradeoff is worth knowing about, because it means rules accuracy depends
entirely on tool calls the model chooses to make.
Concept resolution
Working out which game concepts a message is about runs three paths at once:
- Fuzzy string matching against the 833 concept names.
- Vector similarity between the message embedding and stored concept embeddings.
- A Haiku call.
The results merge with agreement scoring, so a concept found by two paths outranks one proposed by a single path. The model path is the one that invents plausible category names that do not exist. The other two can only return vocabulary that does, so agreement between paths carries information. See taxonomy and tagging for the vocabulary itself.
Context loading
Driven by the flags from classification:
| Flag | What loads |
|---|---|
deck_design | Name, commander, counts, overview, themes, power level, play style, win conditions, notes |
deck_cards | Full oracle text for every card in the deck |
analysis | A summary of stored analysis results for the deck |
knowledge | Nothing today. See below |
Concept definitions load whenever concept resolution returned anything, pulling in definitions for tags on the deck's own cards as well.
Card searching is deliberately absent here. Finding candidate cards is left to the reasoner, which can see what it already has and search for what it lacks.
The knowledge flag is the one to know about. It is wired end to end and has
nothing to read: it is meant to fetch deck-building theory by similarity to your
message, and the classifier still sets it, but the corpus behind it is not built
yet, so the fetch returns nothing and the block is omitted. Strategic advice
today comes from the model rather than from curated theory. That work is in
progress, and the knowledge layer describes where it is
going.
Prompt assembly
Blocks are ordered so the stable ones can be cached, which makes repeat turns about ten times cheaper on those tokens:
- Static instructions.
- Compressed rules.
- Intent framing.
- Deck identity.
- Full deck oracle text.
Then the per-turn blocks: resolved tags, concept definitions, deck notes, knowledge passages, analysis summary, and a line about where in the app you are.
The deck oracle block is not truncated. A 100 card deck contributes its complete text every turn. This is a deliberate cost: rules text is precise, and the tokens are cheap on a cache hit.
Reasoning
Sonnet runs a tool loop, up to five rounds. Available tools:
| Tool | Returns |
|---|---|
get_card | One card by name, with full text and a count of its rulings |
search_cards | Up to 20 cards for a query, re-ranked by similarity to the query |
load_concepts | Definitions for concept slugs |
load_rules | Five matching Comprehensive Rules entries plus their section siblings |
load_card_rulings | Official rulings for named cards |
load_rules_examples | Curated worked examples of interactions |
web_search is added only when you turn it on and the classifier thought it
would help.
search_cards applies your deck's color identity and commander legality by
default, so it will not offer you cards you cannot play. Including ci<= or
f: in the query overrides that.
Its results are ordered by semantic similarity to the query rather than by the database's own order, using the same card embeddings the recommendations surface ranks with. Those embeddings cover oracle text, type line, keywords and semantic tags, so a query for a concept can surface a card tagged with it even when the card never says the word.
If five rounds pass without an answer, the tools are removed and the model is asked to respond with what it found. Those answers carry the least verification of any the system produces, and hard rules questions are where they happen. See grounding and limits.
After the answer
The turn is persisted, token usage logged, timings recorded, and card names in the reply resolved against the database so they render as hover cards.
What this means when an answer is wrong
The pipeline explains most failures.
It ignored something in my deck. The deck was in context if the classifier
set deck_cards. If it decided your question was not about deck contents, the
oracle block was never assembled. Asking a more explicitly deck-shaped question
changes the classification.
It gave up on a rules question. Probably the five round budget. Ask about one interaction at a time.
It missed an obvious card. search_cards returns 20 per call. Narrowing the
query does more than repeating the request.