Skip to main content

See Exactly What the LLM Sees

Debug snapshots are the single most useful debugging tool in Wolffish. They show you the exact system prompt assembled and sent to the LLM on every turn — no abstraction, no summarization, the literal text.

Location

A new snapshot is written by prefrontal.buildContext() on every turn. The filename is the timestamp in YYYY-MM-DD_HH-MM-SS.mmm format (millisecond precision), so lexicographic order is chronological order.
Debug snapshots rotate: the .debug/ directory keeps the 50 most recent snapshots and deletes the oldest beyond that. If you need to preserve one, copy it out of the directory.

What’s Inside

The system prompt is lean by design — a fresh conversation’s prompt is roughly 5,000 tokens. It carries the essentials only; memory, history, files, and the full tool catalog are indexed on disk and retrieved on demand (memory_search, tool_search), never dumped into the prompt. Each snapshot contains:
  • Header — timestamp, estimated tokens, the assembly budget, and the list of sections included
  • Message — the user message this context was built for
  • Sources — every file that fed the prompt, with its category and approximate token cost
  • Prompt — the complete assembled system prompt, verbatim
The prompt itself is built from these sections: Conditional overlays are appended when active: role (<workflow_mode> for the workflow master — including its <workflow_models> catalog of spawnable models — and <workflow_agent> for agents), <channel> formatting (WhatsApp), and <local_model> (the honesty overlay for locally-run models). Conversation history is not part of the system prompt — it travels in the messages array, and long conversations replay as a rolling summary plus the verbatim tail.

Reading a Debug Snapshot

Here is an annotated example:

What to Look For

Check the <capabilities> section. Every installed capability should have one line there — [loaded] means its tool schemas shipped with the request; anything else loads on demand when the model calls tool_search or tool_activate (or calls one of its tools directly, which auto-activates the capability).If your capability is missing from the index entirely, its SKILL.md failed to parse — check the frontmatter. If it’s listed but the model never picks it, make its name, description, and triggers more descriptive: tool_search matches against those terms. To keep a capability always loaded, add it to pinnedCapabilities in config.json.
Memory content is never injected into the prompt — the <memory_map> only tells the model what exists. Recall happens when the model calls memory_search / memory_get / conversation_read during the turn.Check the <memory_map> section first: if the relevant source (episodes, knowledge, conversations) shows records in the right date range, the data is indexed and a search would have found it — the model just didn’t search. If the map shows nothing for that source, check the underlying files under brain/.
Read the <identity> and <prefrontal> sections carefully. The LLM follows these instructions literally. If behavior is unexpected, the instructions might be ambiguous or contradictory. Edit soul.md or agents.md to clarify — never agents.core.md, which is app-managed and overwritten on every launch. Your agents.md wins on any conflict.
The header shows estimated tokens against the assembly budget. A fresh prompt should sit around 5k tokens. If it’s much larger, look for an oversized source in the ## sources list — a bloated soul.md, user.md, or agents.md lands in every prompt. The <capabilities> index stays bounded on its own (it collapses to grouped counts past 60 capabilities), and the <memory_map> is a fixed-size stub.Note the prompt is only part of a request: tool schemas for core + activated capabilities ride the API’s tools parameter, and conversation history rides the messages array.
Tool schemas are not prose in the prompt — they ship via the provider’s tools parameter for the core capabilities plus whatever this conversation has activated (up to 10 non-core activations, LRU-evicted invisibly). The <capabilities> index tells you what’s [loaded].If the model hits an “unknown tool” error, that means not-yet-loaded, not broken — the error itself instructs the model to tool_search. A known-but-unloaded tool called directly just works. If a tool never appears at all, check its capability’s SKILL.md tools: declarations and plugin/index.mjs exports.

The 90% Rule

If Wolffish did something wrong, 90% of the time the answer is in the debug snapshot. The model can only act on what it sees — if the right information wasn’t in the context, the model couldn’t have used it. And under the lean design, “what it sees” includes what it can fetch: check whether the model was told the data existed (<memory_map>, <capabilities>) and simply didn’t go get it.
Start here. Always.