diff --git a/docs/plans/2026-02-13-export-phase-b.md b/docs/plans/2026-02-13-export-phase-b.md
new file mode 100644
index 00000000..87b1fdfc
--- /dev/null
+++ b/docs/plans/2026-02-13-export-phase-b.md
@@ -0,0 +1,762 @@
+# Export Improvements Phase B — Implementation Plan
+
+> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
+
+**Goal:** Add summary block, custom step markers, detail levels (standard/full), and editable preview modal to the export system.
+
+**Architecture:** Backend changes to all 4 export generators (markdown, text, HTML, PSA) + schema additions. Frontend changes to ExportPreviewModal (editable textarea) and SessionDetailPage (detail level dropdown, summary toggle). No migration needed.
+
+**Tech Stack:** Python FastAPI, Pydantic v2, React 19, TypeScript, Tailwind CSS
+
+---
+
+## Task 1: Add Phase B Fields to Backend Schema
+
+**Files:**
+- Modify: `backend/app/schemas/session.py:84-91`
+
+**Step 1: Add `include_summary` and `detail_level` to `SessionExport`**
+
+```python
+class SessionExport(BaseModel):
+ format: str = Field(default="markdown", pattern="^(text|markdown|html|psa)$")
+ include_timestamps: bool = True
+ include_tree_info: bool = True
+ # Phase A
+ include_outcome_notes: bool = True
+ include_next_steps: bool = True
+ max_step_index: Optional[int] = Field(None, ge=1, description="1-based inclusive step cutoff")
+ # Phase B
+ include_summary: bool = False
+ detail_level: Literal["standard", "full"] = "standard"
+```
+
+Note: `Literal` is already imported at line 2.
+
+**Step 2: Run tests to verify no regressions**
+
+Run: `cd /c/Dev/Projects/patherly/backend && python -m pytest tests/test_export.py -v`
+Expected: All existing export tests pass (new fields have defaults, so backward-compatible).
+
+**Step 3: Commit**
+
+```bash
+git add backend/app/schemas/session.py
+git commit -m "feat: add include_summary and detail_level to SessionExport schema"
+```
+
+---
+
+## Task 2: Add Frontend Types for Phase B
+
+**Files:**
+- Modify: `frontend/src/types/session.ts:79-86`
+
+**Step 1: Add `include_summary` and `detail_level` to `SessionExport` interface**
+
+```typescript
+export interface SessionExport {
+ format: 'text' | 'markdown' | 'html' | 'psa'
+ include_timestamps?: boolean
+ include_tree_info?: boolean
+ include_outcome_notes?: boolean
+ include_next_steps?: boolean
+ max_step_index?: number
+ include_summary?: boolean
+ detail_level?: 'standard' | 'full'
+}
+```
+
+**Step 2: Build to verify**
+
+Run: `cd /c/Dev/Projects/patherly/frontend && npx tsc --noEmit`
+Expected: No type errors.
+
+**Step 3: Commit**
+
+```bash
+git add frontend/src/types/session.ts
+git commit -m "feat(frontend): add include_summary and detail_level to SessionExport type"
+```
+
+---
+
+## Task 3: Custom Step Markers (B2) in All 4 Generators
+
+**Files:**
+- Modify: `backend/app/services/export_service.py`
+
+This is a pure backend change. Detect custom steps by `node_id.startswith("custom-")` and prefix the step title with `[CUSTOM]`.
+
+**Step 1: Update `generate_markdown_export`** (line 163)
+
+Change:
+```python
+ lines.append(f"### Step {i}: {question}")
+```
+To:
+```python
+ is_custom = decision.get("node_id", "").startswith("custom-")
+ prefix = "[CUSTOM] " if is_custom else ""
+ lines.append(f"### Step {i}: {prefix}{question}")
+ if is_custom:
+ lines.append("*Custom step added by engineer*")
+```
+
+**Step 2: Update `generate_text_export`** (line 250)
+
+Change:
+```python
+ lines.append(f"\n{i}. {question}")
+```
+To:
+```python
+ is_custom = decision.get("node_id", "").startswith("custom-")
+ prefix = "[CUSTOM] " if is_custom else ""
+ lines.append(f"\n{i}. {prefix}{question}")
+```
+
+**Step 3: Update `generate_html_export`** (line 342)
+
+Change:
+```python
+ html_parts.append(f'
Step {i}: {question}
')
+```
+To:
+```python
+ is_custom = decision.get("node_id", "").startswith("custom-")
+ custom_badge = 'CUSTOM' if is_custom else ''
+ html_parts.append(f'{custom_badge}Step {i}: {question}
')
+```
+
+**Step 4: Update `generate_psa_export`** (line 413)
+
+Change:
+```python
+ line = f"{i}. {question}"
+```
+To:
+```python
+ is_custom = decision.get("node_id", "").startswith("custom-")
+ prefix = "[CUSTOM] " if is_custom else ""
+ line = f"{i}. {prefix}{question}"
+```
+
+**Step 5: Run tests**
+
+Run: `cd /c/Dev/Projects/patherly/backend && python -m pytest tests/test_export.py -v`
+Expected: All pass. (Existing tests don't have custom steps, so markers won't appear — no regressions.)
+
+**Step 6: Commit**
+
+```bash
+git add backend/app/services/export_service.py
+git commit -m "feat: add [CUSTOM] markers to custom steps in all 4 export generators"
+```
+
+---
+
+## Task 4: Command Output Truncation + Detail Levels (B3)
+
+**Files:**
+- Modify: `backend/app/services/export_service.py`
+
+**Step 1: Add `_truncate_command_output` helper** (after `_get_command_output` at line 91)
+
+```python
+def _truncate_command_output(output: str, max_lines: int = 5) -> str:
+ """Truncate command output to max_lines for standard detail level."""
+ lines = output.splitlines()
+ if len(lines) <= max_lines:
+ return output
+ truncated = "\n".join(lines[:max_lines])
+ return f"{truncated}\n*(full output omitted — {len(lines)} lines)*"
+```
+
+**Step 2: Apply truncation in `generate_markdown_export`** (line 168)
+
+Change:
+```python
+ if command_output := _get_command_output(decision):
+```
+To:
+```python
+ if command_output := _get_command_output(decision):
+ if options.detail_level == "standard":
+ command_output = _truncate_command_output(command_output)
+```
+
+**Step 3: Apply truncation in `generate_text_export`** (line 255)
+
+Same pattern — add truncation right after `if command_output := _get_command_output(decision):`:
+```python
+ if options.detail_level == "standard":
+ command_output = _truncate_command_output(command_output)
+```
+
+**Step 4: Apply truncation in `generate_html_export`** (line 347)
+
+Same pattern after `if command_output := _get_command_output(decision):`:
+```python
+ if options.detail_level == "standard":
+ command_output = _truncate_command_output(command_output)
+```
+
+**Step 5: Apply truncation in `generate_psa_export`** (line 421)
+
+Same pattern after `if command_output := _get_command_output(decision):`:
+```python
+ if options.detail_level == "standard":
+ command_output = _truncate_command_output(command_output)
+```
+
+**Step 6: Run tests**
+
+Run: `cd /c/Dev/Projects/patherly/backend && python -m pytest tests/test_export.py -v`
+Expected: All pass (default `detail_level="standard"` but existing test command outputs are short).
+
+**Step 7: Commit**
+
+```bash
+git add backend/app/services/export_service.py
+git commit -m "feat: add command output truncation for standard detail level"
+```
+
+---
+
+## Task 5: Summary Block Generation (B1)
+
+**Files:**
+- Modify: `backend/app/services/export_service.py`
+
+Add summary block generation to all 4 generators. The summary is inserted after metadata, before Evidence/Steps. Only rendered when `options.include_summary is True`.
+
+**Step 1: Add `_build_summary_fields` helper** (after `_get_outcome_label` at line 113)
+
+```python
+def _build_summary_fields(session: Session) -> dict[str, str]:
+ """Build auto-populated summary fields from session data."""
+ tree_name = session.tree_snapshot.get("name", "Troubleshooting Session")
+ tree_desc = session.tree_snapshot.get("description", "")
+ issue = f"{tree_name}: {tree_desc}" if tree_desc else tree_name
+
+ if session.completed_at:
+ status = "Resolved" if getattr(session, "outcome", None) == "resolved" else \
+ f"Completed — {_get_outcome_label(session) or 'Unknown'}"
+ else:
+ step_count = len(session.decisions) if session.decisions else 0
+ status = f"In Progress — paused at step {step_count}" if step_count else "In Progress"
+
+ _raw_notes = getattr(session, 'outcome_notes', None)
+ resolution = (_raw_notes if isinstance(_raw_notes, str) else '').strip() or "[Edit in preview]"
+
+ _raw_next = getattr(session, 'next_steps', None)
+ next_steps = (_raw_next if isinstance(_raw_next, str) else '').strip() or "[Edit in preview]"
+
+ return {
+ "issue": issue,
+ "impact": "[Edit in preview]",
+ "status": status,
+ "resolution": resolution,
+ "next_steps": next_steps,
+ }
+```
+
+**Step 2: Add summary block to `generate_markdown_export`**
+
+Insert after the tree_info block (after line 138, before the scratchpad section):
+
+```python
+ if options.include_summary:
+ summary = _build_summary_fields(session)
+ lines.append("## Summary")
+ lines.append("")
+ lines.append(f"| Field | Details |")
+ lines.append(f"|-------|---------|")
+ lines.append(f"| Issue | {summary['issue']} |")
+ lines.append(f"| Impact | {summary['impact']} |")
+ lines.append(f"| Status | {summary['status']} |")
+ lines.append(f"| Resolution | {summary['resolution']} |")
+ lines.append(f"| Next Steps | {summary['next_steps']} |")
+ lines.append("")
+ lines.append("---")
+ lines.append("")
+```
+
+**Step 3: Add summary block to `generate_text_export`**
+
+Insert after tree_info block (after line 227, before scratchpad section):
+
+```python
+ if options.include_summary:
+ summary = _build_summary_fields(session)
+ lines.append("SUMMARY")
+ lines.append("-" * 20)
+ lines.append(f"Issue: {summary['issue']}")
+ lines.append(f"Impact: {summary['impact']}")
+ lines.append(f"Status: {summary['status']}")
+ lines.append(f"Resolution: {summary['resolution']}")
+ lines.append(f"Next Steps: {summary['next_steps']}")
+ lines.append("")
+```
+
+**Step 4: Add summary block to `generate_html_export`**
+
+Insert after tree_info `` (after line 321, before scratchpad section):
+
+```python
+ if options.include_summary:
+ summary = _build_summary_fields(session)
+ html_parts.append('Summary
')
+ html_parts.append('')
+ for label, value in [("Issue", summary["issue"]), ("Impact", summary["impact"]),
+ ("Status", summary["status"]), ("Resolution", summary["resolution"]),
+ ("Next Steps", summary["next_steps"])]:
+ html_parts.append(f'| {html.escape(label)} | ')
+ html_parts.append(f'{html.escape(value)} |
')
+ html_parts.append('
')
+```
+
+**Step 5: Add summary block to `generate_psa_export`**
+
+Insert after `lines.append("")` on line 394 (after the header, before PROBLEM section):
+
+```python
+ if options.include_summary:
+ summary = _build_summary_fields(session)
+ lines.append("--- SUMMARY ---")
+ lines.append(f"Issue: {summary['issue']}")
+ lines.append(f"Impact: {summary['impact']}")
+ lines.append(f"Status: {summary['status']}")
+ lines.append(f"Resolution: {summary['resolution']}")
+ lines.append(f"Next Steps: {summary['next_steps']}")
+ lines.append("")
+```
+
+**Step 6: Run tests**
+
+Run: `cd /c/Dev/Projects/patherly/backend && python -m pytest tests/test_export.py -v`
+Expected: All pass (summary is opt-in, existing tests don't set `include_summary=True`).
+
+**Step 7: Commit**
+
+```bash
+git add backend/app/services/export_service.py
+git commit -m "feat: add summary block generation to all 4 export generators"
+```
+
+---
+
+## Task 6: Editable Preview Modal (B4)
+
+**Files:**
+- Modify: `frontend/src/components/session/ExportPreviewModal.tsx`
+
+**Step 1: Replace read-only preview with editable textarea and add controls**
+
+Rewrite the component:
+
+```tsx
+import { useState, useEffect } from 'react'
+import { Copy, Download, Check, RotateCcw } from 'lucide-react'
+import { Modal } from '@/components/common/Modal'
+import { cn } from '@/lib/utils'
+
+interface ExportPreviewModalProps {
+ isOpen: boolean
+ onClose: () => void
+ content: string
+ filename: string
+ format: 'markdown' | 'text' | 'html' | 'psa'
+ onDownload: (content: string) => void
+ includeSummary?: boolean
+ onToggleSummary?: (include: boolean) => void
+}
+
+export function ExportPreviewModal({
+ isOpen,
+ onClose,
+ content,
+ filename,
+ format,
+ onDownload,
+ includeSummary = false,
+ onToggleSummary,
+}: ExportPreviewModalProps) {
+ const [copied, setCopied] = useState(false)
+ const [editedContent, setEditedContent] = useState(content)
+
+ // Sync editedContent when content prop changes (new fetch)
+ useEffect(() => {
+ setEditedContent(content)
+ }, [content])
+
+ const handleCopy = async () => {
+ try {
+ await navigator.clipboard.writeText(editedContent)
+ setCopied(true)
+ setTimeout(() => setCopied(false), 2000)
+ } catch (err) {
+ console.error('Failed to copy:', err)
+ }
+ }
+
+ const handleDownload = () => {
+ onDownload(editedContent)
+ onClose()
+ }
+
+ const handleReset = () => {
+ setEditedContent(content)
+ }
+
+ const handleClose = () => {
+ setCopied(false)
+ onClose()
+ }
+
+ const isModified = editedContent !== content
+
+ return (
+
+ {/* Filename, format info, and controls */}
+
+
+ Filename: {filename}
+
+ {format === 'markdown' ? 'Markdown' : format === 'html' ? 'HTML' : format === 'psa' ? 'PSA' : 'Plain Text'}
+
+ {isModified && (
+ (edited)
+ )}
+
+
+ {onToggleSummary && (
+
+ )}
+ {isModified && (
+
+ )}
+
+
+
+ {/* Editable Content */}
+
+ )
+}
+
+export default ExportPreviewModal
+```
+
+Key changes:
+- `` → `