diff --git a/docs/plans/2026-07-28-001-feat-markdown-message-rendering-plan.md b/docs/plans/2026-07-28-001-feat-markdown-message-rendering-plan.md new file mode 100644 index 0000000..a59f06b --- /dev/null +++ b/docs/plans/2026-07-28-001-feat-markdown-message-rendering-plan.md @@ -0,0 +1,446 @@ +--- +title: "feat: Render markdown in agent replies and chat messages" +type: feat +status: completed +date: 2026-07-28 +depth: standard +--- + +# feat: Render markdown in agent replies and chat messages + +## Summary + +Deuce's agent replies and chat messages currently render as raw, unformatted +text. When `@deuce` returns a response full of markdown (`##` headers, `**bold**`, +`###`, numbered lists, code fences), the user sees it all crammed together — +`…what this repo is.## This is the **Unify Agent Workspace**…` — because the +reply is dropped into a plain `
` with no markdown parsing and no
+whitespace preservation.
+
+The fix wires up `react-markdown` + `remark-gfm` — **already in `package.json`
+since the initial commit but never referenced in `src/`** — into a single shared
+` ` — newlines
+ survive, but no markdown is parsed, so `**bold**` and lists stay literal.
+- `react-markdown@^10.1.0` and `remark-gfm@^4.0.1` are dependencies but a repo
+ grep finds **zero references** in `src/`. There is even a leftover
+ `.q-resp code { … }` rule (`src/styles/globals.css:634`) that anticipated
+ markdown code spans. The wiring was simply never done.
+
+**Scope decisions (confirmed with requester):**
+
+- Render markdown across **all message text** via one shared component — agent
+ replies, system notices, and human-typed chat messages.
+- Include **syntax highlighting** for fenced code blocks.
+
+---
+
+## Requirements
+
+- **R1** — Agent replies in the thread drawer render markdown: headers, ordered
+ and unordered lists, bold/italic, inline code, fenced code blocks, blockquotes,
+ tables, and links display as formatted output, not literal syntax.
+- **R2** — Human-typed chat messages and system notices render through the same
+ shared renderer, so formatting is consistent everywhere a message body appears.
+- **R3** — Fenced code blocks are syntax-highlighted with a dark-mode theme that
+ fits the existing Primer palette; an unknown or missing language degrades
+ gracefully to an unhighlighted styled block.
+- **R4** — Rendering is XSS-safe: model- and user-generated markdown cannot
+ inject executable HTML or `javascript:` URLs. Links open in a new tab with safe
+ `rel` attributes.
+- **R5** — The compact task-card summary
+ (`AgentTaskCard.tsx`, terminal state) stays a single line — markdown is stripped
+ to readable plain text so headers/lists don't blow out the card layout.
+- **R6** — No regression to intentionally-verbatim surfaces: tool output
+ (`.q-out` / Bash stdout / diffs) and the terminal/logs panels keep rendering as
+ raw monospace, untouched.
+
+---
+
+## Key Technical Decisions
+
+- **KTD1 — One shared `