diff --git a/CLAUDE.md b/CLAUDE.md index 72cc9b2..5642116 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -263,9 +263,14 @@ the cascade rather than transitioning the wrong item. ## LLM output contract -All phrasing paths emit `{"response":"...","mood":"..."}` (parsed in `replier_llm.go` and -`internal/phraser/llmphraser.go`), with fallback to plain text and the legacy -`{"body","summary"}`. Mood is a fixed enum. Router prompt is a separate contract: +All phrasing paths emit `{"response":"...","mood":"..."}`, with fallback to plain text when +the model skips the JSON. **One parser, `parseResponseMood` in +`internal/phraser/parse.go`**, and every path reaches it: the six `LLMPhraser` methods, +`PhraseWorld`, and `Replier.PhraseReply`, which `cmd/mavend/replier_llm.go` wraps — that file +holds the stub fallback and no parsing of its own. The legacy `{"body","summary"}` fallback +was deleted on 2026-08-06 (V-397): it was the contract before `{"response","mood"}` replaced +it, no prompt asks for that shape, the GBNF cannot emit it, and no test covered it. +Mood is a fixed enum. Router prompt is a separate contract: `[{"intent":, key?, value?, text?, verb?}, ...]`, 7 intents (`fact, reminder, note, query, act, chat, system`). `llm/check_prompt_parity.py` in the training workspace enforces that the Go and relabelling prompts remain identical.