Skip to content

Commit 15c602a

Browse files
alsechemistclaude
andcommitted
fix(format): Claude-style response formatting — worked-example prompt + renderer safety-net
The prior markdown nudge used abstract rules with no worked example, so models still wrapped multi-line commands in single backticks (run-on pills). Driven by reference/HOW_CLAUDE_ORGANIZES_RESPONSES.md + reference/WOLF_MARKDOWN_MECHANISM.md. Must behave identically for ANY model. Diagnosis: - One shared, model-agnostic system prompt (built once in agent/loop.py and passed to whichever adapter) → a single prompt edit covers Ollama + OpenRouter. - Renderer already react-markdown v10, already distinguishing fenced vs inline (className language- + pre→fragment) with language label + copy + overflow-x. The reference's inline-prop sample is for v8 and was NOT followed (would regress). - Root cause = Layer 1 (generation); the missing lever was the worked example. Layer 1 (agent/prompts.py): RESPONSE ORGANIZATION block (direct-answer-first, procedure decomposition, proportionality) + MARKDOWN FORMATTING rules (classify → fenced vs inline, one command per line) + a worked install example (prose + inline <MANAGER_IP> + fenced bash blocks coexisting). Commands written one-per- full-line to avoid Python eating shell backslash-continuations in the "" string. Layer 2 (components/markdown.tsx): looksLikeMisemittedBlock() promotes an inline <code> with a newline or long(>72)+whitespace content to a fenced block, so a weak model that still mis-emits renders a scrollable code box, not a run-on pill. Short refs (ids/IPs/paths/ports/statuses) stay inline pills. Verification (mechanism §6, both providers, real prompt, direct adapter call): OpenRouter/nemotron PASS (11 fenced blocks, 0 inline run-ons); Ollama/qwen3:8b PASS (7 fenced blocks, 0 run-ons). Renderer heuristic validated on 10 inline refs (stay inline) + 3 run-ons (promote) + a 69-char no-space path (stays inline). Dashboard tsc + eslint clean; full backend suite 710 passed; wolf-server restarted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent b3f0b37 commit 15c602a

6 files changed

Lines changed: 794 additions & 27 deletions

File tree

docs/CHANGELOG.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,59 @@ Copy this block and fill in at the start of each session entry:
4949

5050
---
5151

52+
## 2026-06-30 — Claude-style response formatting (Layer 1 generation + Layer 2 renderer, model-agnostic)
53+
54+
**Session type:** claude-code
55+
**Phase:** 6-e (UI/UX, follow-up to the panel/markdown polish below)
56+
**Branch / commit:** main
57+
58+
### Context
59+
The first markdown pass (entry below) added abstract formatting bullets to the
60+
prompt, but Wolf still wrapped whole multi-line command sequences in SINGLE
61+
backticks — rendering as one long run-on pill (operator screenshots). Driven by
62+
two operator-provided reference specs (`reference/HOW_CLAUDE_ORGANIZES_RESPONSES.md`,
63+
`reference/WOLF_MARKDOWN_MECHANISM.md`). Requirement: behave identically for ANY
64+
model (local Ollama Qwen3 or OpenRouter).
65+
66+
### Diagnosis
67+
- **One shared, model-agnostic system prompt.** Built once in
68+
`agent/loop.py` (`Message(system, strategy.system_prompt())`
69+
`prompts.SYSTEM_PROMPT`), passed via `ChatRequest` to whichever adapter
70+
(Ollama / OpenRouter / OpenAI / Anthropic). Adapters only translate messages;
71+
none injects its own prompt. So one prompt edit covers every model.
72+
- **Renderer already on react-markdown v10** (`components/markdown.tsx`),
73+
already distinguishing fenced vs inline (className `language-` + `pre`→fragment)
74+
with language label + copy + `overflow-x-auto`. The reference doc's `inline`-prop
75+
sample is for react-markdown ≤v8 — NOT followed (would regress v10).
76+
- **Root cause = Layer 1 (generation):** the model mis-emitted inline run-ons;
77+
the renderer faithfully rendered them. The missing lever was the **worked
78+
example** (the refs are emphatic it's "not optional").
79+
80+
### What we did
81+
- **Layer 1 (`agent/prompts.py`):** replaced the thin bullets with the full
82+
`RESPONSE ORGANIZATION` block (org §6.1: direct-answer-first, procedure
83+
decomposition, proportionality) + `MARKDOWN FORMATTING` rules (mech §3:
84+
classify→fence vs inline, one command per line) + a **worked install example**
85+
(prose + inline `<MANAGER_IP>` + fenced `bash` blocks coexisting). Adapted the
86+
example to avoid shell `\` line-continuations (a non-raw Python `"""` string
87+
would eat them) — each command on its own full line instead.
88+
- **Layer 2 (`components/markdown.tsx`):** defensive safety-net —
89+
`looksLikeMisemittedBlock()` promotes an inline `<code>` that has a newline or
90+
is long+whitespace (>72 chars) to a fenced block, so even a weak model that
91+
still mis-emits renders a scrollable code box, not a layout-breaking pill.
92+
Genuine short refs (ids/IPs/paths/ports/statuses) stay inline pills.
93+
94+
### Verification
95+
- Acceptance test (mech §6) run directly against BOTH providers with the real
96+
prompt: **OpenRouter (nemotron) PASS** (11 fenced blocks, 0 inline run-ons),
97+
**Ollama (qwen3:8b) PASS** (7 fenced blocks, 0 run-ons). Coexistence + one
98+
command per line confirmed on both.
99+
- Renderer heuristic validated on 10 inline refs (stay inline) + 3 run-ons
100+
(promote) + a 69-char no-space path (stays inline).
101+
- Dashboard tsc + eslint clean; full backend suite green; wolf-server restarted.
102+
103+
---
104+
52105
## 2026-06-30 — UI/UX polish: panel scrolling, wide-content containment, markdown discipline
53106

54107
**Session type:** claude-code

memory/wolf-uiux-claude-grade-standard.md

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,27 @@ undermines trust in Wolf's answers.
3333
- **No `truncate`/"…" where the user wants the whole thing.** Wrap full content
3434
within the box (e.g. Actions "Recent activity" detail line: `break-words` on
3535
its own line, no clamp).
36-
- **Code-block vs inline-code discipline lives in the agent `SYSTEM_PROMPT`**
37-
(`agent/prompts.py``MARKDOWN FORMATTING`): fenced blocks (with a language,
38-
one command per line) for commands / multi-line code / config / structured
39-
payloads; inline code ONLY for short literals in prose (ids, IPs, rule ids,
40-
paths, timestamps); GFM tables for tabular data. Wolf must pick correctly,
41-
like Claude does.
36+
- **Code-block vs inline-code discipline = two layers, both required (specs:
37+
`reference/HOW_CLAUDE_ORGANIZES_RESPONSES.md` + `reference/WOLF_MARKDOWN_MECHANISM.md`).**
38+
- *Layer 1 (generation):* `agent/prompts.py``SYSTEM_PROMPT` carries a
39+
`RESPONSE ORGANIZATION` block + `MARKDOWN FORMATTING` rules **plus a WORKED
40+
EXAMPLE** (a real install procedure: prose + inline `<MANAGER_IP>` + fenced
41+
`bash` blocks). The worked example is the strongest lever — abstract rules
42+
ALONE did not stop the model wrapping multi-line commands in single
43+
backticks; the example fixed it. **The prompt is model-agnostic:** it is
44+
built ONCE in `agent/loop.py` (`strategy.system_prompt()`) and passed to
45+
whichever adapter, so ONE edit covers Ollama AND OpenRouter (and any
46+
provider). NEVER add per-provider prompts. NB: `SYSTEM_PROMPT` is a non-raw
47+
Python `"""` string — shell `\` line-continuations get eaten; write each
48+
command on its own full line.
49+
- *Layer 2 (rendering):* `components/markdown.tsx` is **react-markdown v10**
50+
(no `inline` prop — the doc's `inline`-prop sample is for ≤v8; do NOT use
51+
it). It distinguishes fenced (className `language-` / strip `pre`) → code box
52+
with language label + copy + `overflow-x-auto`, vs inline → pill. Safety-net:
53+
`looksLikeMisemittedBlock()` promotes a long(>72)+whitespace or multiline
54+
inline span to a fenced block so a weak model still renders cleanly.
55+
- Verified PASS on BOTH nemotron (OpenRouter) and qwen3:8b (Ollama) via a
56+
direct-adapter acceptance test (mech doc §6).
4257

4358
Source of truth for the shipped fixes: `services/dashboard/components/`
4459
(`citations-panel.tsx`, `chat-sidebar.tsx`, `message-thread.tsx`, `markdown.tsx`),
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
# How Claude Organizes and Structures Its Responses
2+
3+
> **What this document is.** This is a description of the *observable behavior* of how
4+
> Claude structures, organizes, and formats its responses — the patterns you can see in
5+
> the output and reproduce. It is a companion to `WOLF_MARKDOWN_MECHANISM.md`, which
6+
> covers the concrete code-block/inline-code implementation. Read this one for the
7+
> *principles* of good response organization; read that one for the *mechanism* to ship it.
8+
>
9+
> **What this document is NOT.** It is not a description of Claude's internal architecture,
10+
> weights, or training. No one can hand you "the exact internal machinery" honestly —
11+
> that behavior emerges from training and isn't a documented classifier anyone can point
12+
> to. Everything below is framed as *behavior to reproduce*, because that is what is real,
13+
> verifiable, and implementable. Wolf can implement observable behavior. It cannot
14+
> implement a fictional internal mechanism. Treat this as a behavioral spec, not a
15+
> reverse-engineering of a black box.
16+
17+
---
18+
19+
## 1. The core principle: structure follows the shape of the answer
20+
21+
Well-organized responses are not formatted *decoratively*. The structure mirrors the
22+
logical shape of the content. The single question that drives every formatting decision:
23+
24+
> **"What is the natural shape of this answer, and what is the lightest formatting that
25+
> makes that shape visible?"**
26+
27+
A definition wants a sentence. A comparison wants a table or paired paragraphs. A
28+
procedure wants numbered steps. A command wants a code block. A short reference inside a
29+
sentence wants inline code. The format is chosen to *reveal* the structure already present
30+
in the content — never to dress it up.
31+
32+
The corollary, which matters just as much: **when content has no structure, impose none.**
33+
A simple question gets a couple of plain sentences. Over-formatting a simple answer
34+
(headers, bullets, bold scattered everywhere) is as much a failure as under-formatting a
35+
complex one. Good organization is proportional to the actual complexity of the answer.
36+
37+
---
38+
39+
## 2. The layered decision: what to say → how to order it → how to mark it up
40+
41+
A sophisticated response is built in three conceptual passes (they happen together in
42+
practice, but it helps to separate them):
43+
44+
### 2.1 Selection — decide what actually belongs
45+
- Answer the question that was asked, not an adjacent one.
46+
- Lead with the part the reader most needs; cut throat-clearing and filler.
47+
- Include caveats and edge cases only where they change what the reader would do.
48+
- Length is governed by the question. A factual question gets a short answer. A request
49+
to "explain in depth" earns expansion. Padding a short answer to look thorough is a
50+
failure mode.
51+
52+
### 2.2 Ordering — sequence it the way a reader consumes it
53+
- **Most important first.** The direct answer comes before the elaboration, not after a
54+
long wind-up. (The reader should be able to stop after the first paragraph and still
55+
have the answer.)
56+
- **Dependencies before dependents.** Prerequisites, definitions, and setup come before
57+
the steps that rely on them.
58+
- **Chronological for procedures.** Step 1 does in fact come before step 2.
59+
- **General before specific**, then drill into the particular case.
60+
61+
### 2.3 Markup — mark up only to expose that order
62+
- Prose for explanation and reasoning.
63+
- Numbered lists for ordered steps; bullets for unordered sets of peers.
64+
- Headers only when the answer has genuinely distinct sections worth navigating.
65+
- Bold sparingly, for the one or two things the eye should catch first.
66+
- Code blocks for commands/code/config; inline code for short in-sentence references.
67+
- Tables for true comparisons across a shared set of dimensions.
68+
69+
The markup layer is the *last* decision, not the first. Structure is decided by content;
70+
markup just makes the decided structure visible.
71+
72+
---
73+
74+
## 3. The orchestration patterns (the "sophisticated" part)
75+
76+
These are the recurring shapes that make a response feel organized rather than like a wall
77+
of text. Each is a response to a *type* of question.
78+
79+
### 3.1 Direct-answer-first
80+
Open with the actual answer in the first sentence or two. Then explain, qualify, or expand.
81+
The reader never has to dig for the payload. Even a long answer should be readable as
82+
"answer, then why."
83+
84+
### 3.2 Procedure decomposition
85+
For anything step-based (installation, configuration, a how-to):
86+
- A numbered list, one step per number.
87+
- Each step's *explanation* in prose, outside any code block.
88+
- Each step's *command(s)* in their own fenced code block directly under the explanation.
89+
- A short verification or "you should now see X" at the end.
90+
This is exactly the pattern formalized in `WOLF_MARKDOWN_MECHANISM.md`.
91+
92+
### 3.3 Comparison structure
93+
When weighing options: state the dimensions being compared, then present each option
94+
against the same dimensions (a table if the dimensions are uniform; paired paragraphs if
95+
they need nuance). End with a recommendation or a "depends on X" if there's no single
96+
winner.
97+
98+
### 3.4 Concept explanation
99+
Definition first, then how it works, then an example or analogy, then edge cases or
100+
caveats. The example is doing real work — it grounds the abstraction — so it comes early,
101+
not as an afterthought.
102+
103+
### 3.5 Mixed-mode coexistence
104+
The hallmark of a polished response: prose, inline code, fenced blocks, and lists
105+
**coexist naturally in one answer**, each carrying the part of the message it's best for.
106+
Prose carries reasoning; an inline `<PLACEHOLDER>` highlights a value to replace; a fenced
107+
block holds the command; a numbered list sequences the steps. This interleaving is what
108+
makes a response feel dynamic and considered rather than monotonous.
109+
110+
### 3.6 Honest-uncertainty structure
111+
When the answer isn't fully known, the structure shows it: state what is known plainly,
112+
flag what is uncertain explicitly, and don't smooth over the gap with confident prose.
113+
Organization includes being clear about the *epistemic status* of each part, not just its
114+
logical place.
115+
116+
---
117+
118+
## 4. Tone and register as part of organization
119+
120+
Structure isn't only visual layout — register is part of it:
121+
122+
- **Match depth to the asker.** A beginner question gets more scaffolding and fewer
123+
assumed terms; an expert question gets density and precision. The same fact is organized
124+
differently for different readers.
125+
- **One voice throughout.** Don't swing between terse and verbose within one answer without
126+
reason.
127+
- **Caveats are brief and placed where they matter**, not piled into a defensive preamble.
128+
- **No filler framing.** Skip "Great question!", "I'd be happy to help", and long
129+
restatements of the question. Get to the content.
130+
131+
---
132+
133+
## 5. What to AVOID (anti-patterns that read as disorganized)
134+
135+
- **Wall of text:** a long answer with no paragraph breaks, no steps, no structure, when
136+
the content clearly has parts.
137+
- **Over-formatting:** headers and bullets and bold on a three-sentence answer. Formatting
138+
noise obscures rather than reveals.
139+
- **Burying the answer:** three paragraphs of preamble before the thing the reader asked.
140+
- **Format/content mismatch:** a numbered list for things that aren't sequential; a code
141+
block for prose; inline code for a ten-line script; a table for two items that don't
142+
share dimensions.
143+
- **Inconsistent depth:** lavishing detail on a trivial step and one line on the hard step.
144+
- **Decorative structure:** sections and headers that don't correspond to real divisions
145+
in the content.
146+
147+
---
148+
149+
## 6. How to encode this behavior in Wolf
150+
151+
Two practical levers, the same two as in the mechanism document:
152+
153+
### 6.1 System-prompt guidance (the generation side)
154+
Add response-organization guidance to Wolf's system prompt alongside the markdown rules.
155+
The principles above translate into directives the model can follow:
156+
157+
```text
158+
## Response organization
159+
160+
Structure every response so its layout mirrors the logical shape of the answer. Use the
161+
lightest formatting that makes that shape clear; impose no structure on simple answers.
162+
163+
- Lead with the direct answer, then explain. Never bury the answer under preamble.
164+
- Order content the way a reader consumes it: most important first, prerequisites before
165+
the steps that need them, chronological for procedures, general before specific.
166+
- Choose markup to expose structure, not to decorate:
167+
- prose for explanation and reasoning
168+
- numbered lists for ordered steps, bullets for unordered peer items
169+
- headers only when the answer has genuinely distinct, navigable sections
170+
- bold sparingly, for the one or two things the eye should catch first
171+
- fenced code blocks for commands/code/config; inline code for short in-sentence refs
172+
- tables only for true comparisons across a shared set of dimensions
173+
- Let prose, inline code, fenced blocks, and lists coexist in one answer, each carrying
174+
the part of the message it fits best.
175+
- Match depth and vocabulary to the apparent expertise of the asker.
176+
- Skip filler framing ("Great question", restating the prompt). Get to the content.
177+
- When something is uncertain, say so plainly rather than smoothing it over.
178+
179+
Proportionality rule: formatting complexity should match content complexity. A simple
180+
question gets a couple of plain sentences. A multi-part or procedural question earns
181+
sections, steps, and code blocks.
182+
```
183+
184+
### 6.2 Worked examples (the strongest lever)
185+
Models reproduce a demonstrated pattern far more reliably than they follow abstract rules.
186+
Put 1–2 short worked examples in the prompt showing the *shape* you want — e.g. one
187+
procedure (direct answer → numbered steps with code blocks → verification) and one simple
188+
factual answer (two plain sentences, no formatting). The contrast teaches proportionality:
189+
it shows both when to add structure and when to withhold it.
190+
191+
### 6.3 Rendering (the display side)
192+
Response organization only lands if the renderer faithfully displays the structure the
193+
model emits — numbered lists as lists, headers as headers, fenced blocks as code boxes,
194+
inline code as pills. That rendering is specified in `WOLF_MARKDOWN_MECHANISM.md`. The two
195+
documents are complementary: this one governs *what structure to produce*; that one governs
196+
*how to render it*.
197+
198+
---
199+
200+
## 7. How the two documents fit together
201+
202+
| Question | Document |
203+
|---|---|
204+
| *What* structure should a response have, and how should it be organized? | **This document** |
205+
| *How* do I make code blocks vs inline code work, and render them like Claude? | `WOLF_MARKDOWN_MECHANISM.md` |
206+
207+
Feed Claude Code both. This document sets the organizational principles and the
208+
response-organization prompt block; the mechanism document provides the concrete
209+
generation rules and the renderer. Together they produce responses that are both
210+
well-organized (this doc) and correctly formatted and displayed (that doc).
211+
212+
---
213+
214+
## 8. A closing note on honesty
215+
216+
If you or Claude Code want to know "is this literally how Claude works inside?" — the
217+
honest answer is that this describes *reproducible behavior*, not internal architecture.
218+
That distinction is a feature, not a limitation: behavior is what you can observe, specify,
219+
test, and implement. An internal mechanism you can't verify would be guesswork you couldn't
220+
act on. Implement the behavior, test against the acceptance criteria in the mechanism
221+
document, and Wolf's responses will be organized and formatted the way you want —
222+
regardless of what any model's internals happen to look like.

0 commit comments

Comments
 (0)