---
phase: 23
slug: brainstormer-flow
status: draft
shadcn_initialized: true
preset: new-york / neutral / css-variables
created: 2026-04-01
---

# Phase 23 — UI Design Contract

> Visual and interaction contract for Phase 23: Brainstormer Flow.
> Generated by gsd-ui-researcher. Verified by gsd-ui-checker.

---

## Design System

| Property | Value | Source |
|----------|-------|--------|
| Tool | shadcn/ui | `ui/components.json` — unchanged from Phase 21/22 |
| Style | new-york | `ui/components.json` |
| Base color | neutral | `ui/components.json` |
| CSS variables | true | `ui/components.json` |
| Component library | Radix UI (via shadcn new-york) | `ui/components.json` |
| Icon library | lucide-react ^0.574.0 | `ui/components.json` |
| Font | System UI (`font-sans`, inherited) | `ui/src/index.css` |

**Existing shadcn components available (no install needed):**
`avatar`, `badge`, `button`, `card`, `checkbox`, `collapsible`, `command`, `dialog`, `dropdown-menu`, `input`, `label`, `popover`, `scroll-area`, `select`, `separator`, `sheet`, `skeleton`, `tabs`, `textarea`, `tooltip`

**Existing custom components to reuse/extend:**
- `ChatMessage.tsx` — extend with `role === "system"` rendering branch for handoff/spec/status-update messages
- `ChatPanel.tsx` — extend with Brainstormer default agent selection on new conversation
- `AgentIcon` (from `AgentIconPicker.tsx`) — used for Brainstormer identity in all message identity bars
- `agentRoleColors` (from `agent-role-colors.ts`) — `general` role maps to `text-slate-600 dark:text-slate-400` for Brainstormer avatar
- `issuesApi` — referenced from `ui/src/api/issues` for task creation link-out

**New DB migration required:**
- Add `message_type` column to `chat_messages` table: `text("message_type")` — values: `null` (normal), `"handoff"`, `"spec_card"`, `"task_created"`, `"status_update"`. Used by Phase 23 system message rendering to determine which specialized card to display.

---

## Layout Contract

### Layout Unchanged

The overall layout established in Phase 21 and unmodified in Phase 22 remains:

```
[ CompanyRail ] [ Sidebar ] [ <main> ] [ ChatPanel ] [ PropertiesPanel ]
```

Phase 23 adds new UI surfaces **inside** `ChatPanel` and `ChatMessageList` only. No layout-level changes.

### Default Brainstormer Selection (AGENT-01)

When the user opens a **new conversation** (no prior messages), the `ChatPanel` auto-selects the workspace agent with `role === "general"` as the default. The `ChatAgentSelector` renders this agent immediately without user action.

**Selection precedence:**
1. If a `general` role agent exists in the workspace — select it
2. If multiple `general` agents exist — select the first by `createdAt` ascending
3. If no `general` agent exists — fall back to the first agent alphabetically (same as current behavior)
4. Auto-selection fires only when `activeAgentId === null` and the conversation has zero messages

The auto-selected agent is visually indistinguishable from a manually selected agent — the `ChatAgentSelector` shows its icon + name normally.

### Spec Card Layout (AGENT-02)

When the Brainstormer completes its questioning flow, it emits a **spec card** as a `system` role message with `message_type: "spec_card"`. The `ChatMessage` component renders a `ChatSpecCard` instead of a markdown message.

```
┌─────────────────────────────────────────────┐
│  [Brain icon]  Brainstormer — Spec Ready     │  ← identity bar (standard)
├─────────────────────────────────────────────┤
│  What: {what field text}                     │
│  Why: {why field text}                       │
│  Constraints: {constraints field text}       │
│  Success: {success field text}               │
├─────────────────────────────────────────────┤
│  [ Send to PM ]  [ Edit ]  [ Save as Draft ] │  ← action row
└─────────────────────────────────────────────┘
```

- Container: `rounded-lg border border-border bg-card p-4 max-w-[480px]` — uses `bg-card` (secondary 30% surface) to visually distinguish spec from normal prose
- Section labels ("What", "Why", "Constraints", "Success"): `text-[11px] font-semibold uppercase tracking-wide text-muted-foreground` — same weight/size pattern as Phase 22 timestamps
- Section content: `text-[15px] font-normal text-foreground leading-relaxed` — standard body
- Vertical rhythm between sections: `space-y-4` (16px gaps)
- Action row: `flex gap-2 pt-4 border-t border-border mt-4` — separated from content with a border
- "Send to PM" button: `variant="default" size="sm"` — primary action
- "Edit" button: `variant="outline" size="sm"` — secondary action
- "Save as Draft" button: `variant="ghost" size="sm"` — tertiary action

### Handoff Indicator Layout (AGENT-05, CHAT-09)

When the user clicks "Send to PM", a **handoff message** is inserted as a `system` role message with `message_type: "handoff"`. The `ChatMessage` component renders a `ChatHandoffIndicator`.

```
┌───────────────────────────────────────────────┐
│  ──  Brainstormer → PM: spec handed off  ──   │  ← separator-style indicator
└───────────────────────────────────────────────┘
```

- Container: `flex items-center gap-3 py-2 text-[13px] text-muted-foreground`
- Left and right: `<hr className="flex-1 border-border" />` — lines flanking the label
- Center label: `whitespace-nowrap` — "Brainstormer → PM" + brief description (see Copywriting)
- Arrow character: `→` unicode, not a Lucide icon — matches the simple prose style
- No background, no border-radius — this is a separator, not a card

### Task Created Notification Layout (AGENT-03, AGENT-06)

When the PM agent creates Nexus issues from the spec, a `system` role message with `message_type: "task_created"` appears. The `ChatMessage` component renders a `ChatTaskCreatedBadge`.

```
[ #123  Issue title text  →  View task ]
```

- Container: `inline-flex items-center gap-2 rounded-md border border-border bg-card px-3 py-1 text-[13px]`
- Issue ID badge: `text-[11px] font-semibold text-muted-foreground` — e.g. "T-123"
- Issue title: `text-[13px] text-foreground`
- "View task" link: `text-primary underline-offset-2 hover:underline` — navigates to the issue detail page
- Multiple tasks from one spec: render one `ChatTaskCreatedBadge` per task, stacked vertically with `gap-2` wrapper

### Status Update Notification Layout (AGENT-07)

When an Engineer or Generalist completes a task, a `system` role message with `message_type: "status_update"` appears. The `ChatMessage` component renders a `ChatStatusUpdateBadge`.

```
[ ✓  Engineer completed T-123: Issue title ]
```

- Container: `inline-flex items-center gap-2 rounded-md border border-border bg-card px-3 py-1 text-[13px]`
- Icon: `CheckCircle2` (lucide), 14×14px, `text-green-500 dark:text-green-400` — indicates completion
- Text: `text-[13px] text-foreground` — agent name + action + task reference
- Task reference: `text-primary underline-offset-2 hover:underline` — link to issue detail

---

## Spacing Scale

Inherited from Phase 21 and Phase 22. No new tokens for Phase 23.

| Token | Value | Phase 23 Usage |
|-------|-------|----------------|
| xs | 4px | Section label letter-spacing, gap within inline badges |
| sm | 8px | Action button gap in spec card row (`gap-2`), badge padding |
| md | 16px | Spec card padding (`p-4`) |
| lg | 24px | (no new usage) |
| xl | 32px | (no new usage) |

**New spacing values (Phase 23 only):**
- Spec card section vertical rhythm: `space-y-4` (16px) — between What/Why/Constraints/Success blocks
- Spec card action row top: `pt-4` (16px) — from content to action row border
- Task badge height: `py-1` (6px top/bottom) — compact inline badge
- Handoff indicator vertical: `py-2` (8px) — matches section separator rhythm

---

## Typography

All inherited from Phase 21 and 22. No new type tokens for Phase 23.

| Role | Size | Weight | Line Height | Usage |
|------|------|--------|-------------|-------|
| Body / message text | 15px (0.9375rem) | 400 | 1.6 | Spec card content, chat message prose (unchanged) |
| Label / UI chrome | 13px (0.8125rem) | 400 | 1.4 | Task badge text, handoff indicator label, agent selector (unchanged) |
| Spec section label | 11px (0.6875rem) | 600 | 1.4 | "What", "Why", "Constraints", "Success" labels in spec card |
| Message timestamp | 11px (0.6875rem) | 400 | 1.4 | Identity bar timestamps (Phase 22, unchanged) |

**Weights used:** 400 (regular) and 600 (semibold). No additional weights. Same constraint as Phase 21/22.

**Spec section label note:** The 11px / 600 uppercase-tracked label pattern matches the existing `text-[11px] text-muted-foreground` timestamp pattern from Phase 22. The semibold weight at 11px + `uppercase tracking-wide` provides adequate differentiation from surrounding 15px prose. No new weight is introduced.

---

## Color

All values inherited from Phase 21 CSS variable system. No new color variables introduced.

| Role | Catppuccin Mocha | Tokyo Night | Catppuccin Latte | Phase 23 Usage |
|------|-----------------|-------------|-----------------|----------------|
| Dominant (60%) | `--background` #1e1e2e | `--background` #1a1b26 | `--background` #eff1f5 | Unchanged |
| Secondary (30%) | `--card` #181825 | `--card` #16161e | `--card` #e6e9ef | Spec card background, task badge background, status badge background |
| Accent (10%) | `--accent` #45475a | `--accent` #3b4261 | `--accent` #bcc0cc | Hovered rows (unchanged) |
| Primary | `--primary` | `--primary` | `--primary` | "Send to PM" button, task/status reference links |
| Destructive | `--destructive` | `--destructive` | `--destructive` | Not used in Phase 23 |
| Muted text | `--muted-foreground` | `--muted-foreground` | `--muted-foreground` | Handoff indicator label, spec section labels, task ID badges |

**Accent reserved for (unchanged from Phase 22):**
1. Hovered conversation list row
2. Currently active/selected conversation row
3. Code block toolbar background on hover
4. Input focus-within ring
5. Slash command popover highlighted item

**Task completion check icon:** `text-green-500 dark:text-green-400` — same Tailwind semantic color pattern as Phase 22 agent role colors. Sufficient contrast in all three themes.

**Brainstormer avatar color:** `text-slate-600 dark:text-slate-400` — mapped from `agentRoleColors["general"]` in the existing `agent-role-colors.ts` utility. No new color needed.

**Spec card border:** `border-border` — resolves correctly via CSS variable in all three themes.

---

## Component Inventory

New components to build in Phase 23:

| Component | shadcn base | Notes |
|-----------|-------------|-------|
| `ChatSpecCard.tsx` | `button`, `card` | Spec fields (What/Why/Constraints/Success) + action row; rendered inside `ChatMessage` when `messageType === "spec_card"` |
| `ChatHandoffIndicator.tsx` | none | Separator-style indicator; `flex items-center gap-3` with flanking `<hr>` elements |
| `ChatTaskCreatedBadge.tsx` | none | Inline badge for a single created task; receives `taskId`, `taskTitle`, `taskUrl` props |
| `ChatStatusUpdateBadge.tsx` | none | Inline badge for task completion; receives `agentName`, `taskId`, `taskTitle`, `taskUrl` props |
| `useBrainstormerDefault.ts` | none | Hook: queries workspace agents, returns the `general` role agent ID for auto-selection on new conversations |

**Existing components to modify:**

| Component | Change |
|-----------|--------|
| `ChatMessage.tsx` | Add rendering branch for `messageType` prop: `"spec_card"` → `ChatSpecCard`, `"handoff"` → `ChatHandoffIndicator`, `"task_created"` → `ChatTaskCreatedBadge`, `"status_update"` → `ChatStatusUpdateBadge`, `null` → existing markdown/bubble rendering |
| `ChatPanel.tsx` | Wire `useBrainstormerDefault` to set `activeAgentId` when `activeConversationId === null` and `activeAgentId === null` |

**DB migration component:**
- `server/src/db/migrations/XXXX_add_message_type.ts` — adds `message_type text` nullable column to `chat_messages` table

**Icons (lucide-react) — Phase 23 additions:**
- `CheckCircle2` — task completion status badge
- `Brain` — recommended default icon for the Brainstormer agent (user configures, but `brain` is a valid `AGENT_ICON_NAME`)

**All icons from Phase 21 and Phase 22 remain unchanged.**

---

## Interaction Contract

### Brainstormer Default Agent (AGENT-01)

| Interaction | Behavior |
|-------------|---------|
| User opens new conversation (no messages) | `useBrainstormerDefault` resolves `general` role agent; `ChatAgentSelector` reflects that agent immediately |
| User changes agent mid-flow | Normal agent selector behavior (Phase 22); no lock-in to Brainstormer |
| No `general` agent in workspace | Fall back to first agent alphabetically; no error state; log warning to console |

### Structured Questioning Flow (AGENT-02)

The Brainstormer agent's structured questioning flow is entirely **server-side** (system prompt + LLM). The UI has no state machine for question steps — it renders the agent's streamed responses normally via the existing `ChatMessage` + `ChatMarkdownMessage` pipeline.

The spec card is produced when the LLM outputs a message with `message_type: "spec_card"` and a structured JSON body in `content`. The server parses this and stores it as a `system` role message. The UI detects `messageType === "spec_card"` and renders `ChatSpecCard`.

| Interaction | Behavior |
|-------------|---------|
| Brainstormer streams a clarifying question | Standard streaming rendering (Phase 22 pipeline); no special UI |
| User answers a question | Standard message send; no special UI |
| Brainstormer produces spec card | `message_type: "spec_card"` message appears; `ChatSpecCard` renders inline in message thread |

### Spec Card Actions (AGENT-02)

| Interaction | Behavior |
|-------------|---------|
| Click "Send to PM" | Optimistic: spec card action buttons disabled immediately; `ChatHandoffIndicator` appended to thread; POST to `/api/conversations/:id/handoff` with spec content and `targetRole: "pm"` |
| Click "Edit" | Spec card enters edit mode: each field (What/Why/Constraints/Success) becomes an editable `<textarea>`; "Save changes" and "Discard" buttons replace the original action row |
| Click "Save changes" (in edit mode) | PATCH spec card message with updated content; reverts to read-only spec card |
| Click "Discard" (in edit mode) | Revert to read-only spec card; no server call |
| Click "Save as Draft" | Spec card persists as-is; no handoff action; action buttons remain; `[Draft]` badge appended to spec card header in `text-muted-foreground text-[11px]` |
| "Send to PM" succeeds | PM agent begins streaming a response; `ChatHandoffIndicator` already in thread; PM's response follows below it |
| "Send to PM" fails | Toast: "Could not send to PM. Try again."; spec card action buttons re-enabled |

### Handoff Indicator (AGENT-05, CHAT-09)

| Interaction | Behavior |
|-------------|---------|
| Handoff message renders | `ChatHandoffIndicator` is non-interactive — display only; no click, no hover state |
| Multiple handoffs in one conversation | Each handoff gets its own separator row in chronological position |

### Task Created (AGENT-03, AGENT-06)

| Interaction | Behavior |
|-------------|---------|
| Task badge renders | `ChatTaskCreatedBadge` is inline; "View task" link navigates via React Router to the issue detail page |
| Multiple tasks from one spec | Multiple `ChatTaskCreatedBadge` elements stacked vertically |
| Task ID not yet known (optimistic) | Badge shows "Creating task..." in `text-muted-foreground` until `taskId` is available; no spinner — inline text is sufficient |

### Status Update (AGENT-07)

| Interaction | Behavior |
|-------------|---------|
| Status update message renders | `ChatStatusUpdateBadge` is inline read-only; "View task" link navigates to issue detail |
| Multiple status updates | Each is a separate `system` message in the thread; rendered independently |

### Spec Card Edit Mode

| Interaction | Behavior |
|-------------|---------|
| Click field textarea | Standard browser focus; no special behavior |
| Tab between fields | Natural tab order: What → Why → Constraints → Success → Save → Discard |
| Escape in edit mode | Discard changes (same as "Discard" button click) |
| Save with all fields empty | "Save changes" button disabled when all four fields are empty |
| Save with some fields empty | Allowed — partial specs are valid |

---

## Copywriting Contract

All Phase 21 and Phase 22 copy is preserved unchanged. Phase 23 additions:

| Element | Copy | Notes |
|---------|------|-------|
| Spec card header | "Spec Ready" | Preceded by Brainstormer identity bar; no separate heading needed |
| Spec card section label: What | "What" | `text-[11px] uppercase tracking-wide text-muted-foreground` |
| Spec card section label: Why | "Why" | Same style |
| Spec card section label: Constraints | "Constraints" | Same style |
| Spec card section label: Success | "Success" | Same style |
| Spec card "Send to PM" button | "Send to PM" | `variant="default" size="sm"` |
| Spec card "Edit" button | "Edit" | `variant="outline" size="sm"` |
| Spec card "Save as Draft" button | "Save as Draft" | `variant="ghost" size="sm"` |
| Spec card "Save changes" button (edit mode) | "Save changes" | `variant="default" size="sm"` |
| Spec card "Discard" button (edit mode) | "Discard" | `variant="ghost" size="sm"` |
| Spec card draft badge | "[Draft]" | `text-[11px] text-muted-foreground ml-2`; appended to Brainstormer name in identity bar |
| Handoff indicator label | "Brainstormer → PM: spec handed off" | `text-[13px] text-muted-foreground`; `→` is unicode, not icon |
| Task created badge: creating state | "Creating task..." | Shown before `taskId` is available |
| Task created badge: view link | "View task" | `text-primary underline-offset-2 hover:underline` |
| Status update: completion | "{agentName} completed {taskId}" | e.g. "Engineer completed T-123"; task title truncated to 40 chars with ellipsis |
| Status update: view link | "View task" | Same style as task created badge |
| Send to PM failure toast | "Could not send to PM. Try again." | Standard toast pattern; no new toast component needed |
| Spec card edit field placeholder: What | "What should be built?" | `textarea` placeholder; shown when field is empty |
| Spec card edit field placeholder: Why | "Why is this important?" | `textarea` placeholder |
| Spec card edit field placeholder: Constraints | "Any constraints or requirements?" | `textarea` placeholder |
| Spec card edit field placeholder: Success | "How will success be measured?" | `textarea` placeholder |

**Tone:** Direct, functional, no corporate language. Consistent with Phase 21/22.

---

## States and Loading

| Component | Loading state | Empty state | Error state | Notes |
|-----------|--------------|-------------|-------------|-------|
| `ChatSpecCard` | n/a | n/a | "Could not render spec." in `text-destructive text-[13px]` — shown if content cannot be parsed | Fallback if JSON parse fails |
| `ChatHandoffIndicator` | n/a | n/a | n/a | Display only; no async operation |
| `ChatTaskCreatedBadge` | "Creating task..." inline text | n/a | "Task creation failed." in `text-destructive text-[13px]` with retry link | Retry link: `text-primary underline cursor-pointer` |
| `ChatStatusUpdateBadge` | n/a | n/a | n/a | Display only; task link navigates if available |
| `useBrainstormerDefault` | `activeAgentId` stays `null` while agents load | Falls back to first agent | Silent fallback (no error UI — agent selector shows "Select agent" fallback) | — |

**Optimistic updates:**
- "Send to PM" click: `ChatHandoffIndicator` appended immediately before API call completes; spec card buttons disabled. On API failure, `ChatHandoffIndicator` is removed and buttons re-enabled with failure toast.
- Task creation: `ChatTaskCreatedBadge` appears with "Creating task..." state; updates in place when `taskId` resolves.

---

## Theme Integration Contract

Phase 23 extends Phase 21/22's zero-new-plumbing approach:

- Spec card uses `bg-card` and `border-border` — resolves correctly in all three themes via CSS variables
- Task/status badges use `bg-card` and `border-border` — same
- Handoff separator uses `border-border` for the `<hr>` lines — correct in all themes
- `CheckCircle2` completion icon uses `text-green-500 dark:text-green-400` — same Tailwind semantic pattern as Phase 22 agent role colors
- No hardcoded hex colors introduced in Phase 23

**THEME-01 / THEME-02 checklist (Phase 23 surfaces):**
- `ChatSpecCard`: `bg-card`, `border-border`, `text-foreground`, `text-muted-foreground` — all CSS variables
- `ChatHandoffIndicator`: `text-muted-foreground`, `border-border` — CSS variables
- `ChatTaskCreatedBadge`: `bg-card`, `border-border`, `text-foreground`, `text-primary` — CSS variables
- `ChatStatusUpdateBadge`: same as task badge + `text-green-500 dark:text-green-400`

---

## Accessibility

Inherits all Phase 21 and Phase 22 accessibility contracts. Phase 23 additions:

| Concern | Requirement |
|---------|-------------|
| Spec card | `role="region"` with `aria-label="Specification"` |
| Spec card action buttons | Standard labeled buttons — no icon-only ambiguity |
| Spec card edit textareas | Each has an explicit `aria-label`: "What to build", "Why it matters", "Constraints", "Success criteria" |
| Handoff indicator | `aria-label="Agent handoff from Brainstormer to PM"` on the container; `aria-hidden="true"` on the `<hr>` decorators |
| Task created badge | `role="status"` on the badge container; "View task" link has `aria-label="View task {taskId}"` |
| Status update badge | `role="status"` on the badge container; same link labeling pattern |
| System messages in thread | `aria-live="polite"` on `ChatMessageList` (established Phase 21) handles announcement of spec cards, handoffs, task badges — no new plumbing needed |
| Spec card "Send to PM" disabled state | `aria-disabled="true"` when action is in-flight; `aria-busy="true"` on the card region |

---

## Animation and Motion

Inherits Phase 21 and Phase 22 animation contracts. Phase 23 additions:

| Element | Animation | Duration | Easing | CSS |
|---------|-----------|----------|--------|-----|
| Spec card appear | `animate-in fade-in slide-in-from-bottom-2` | 200ms | `ease-out` — shadcn default |
| Handoff indicator appear | `animate-in fade-in` | 150ms | `ease-out` |
| Task badge appear | `animate-in fade-in slide-in-from-bottom-1` | 150ms | `ease-out` |
| Status badge appear | Same as task badge | 150ms | `ease-out` |
| Spec card edit mode toggle | No animation — immediate swap; avoids layout shift (matches Phase 22 edit mode pattern) | — | — |
| "Creating task..." → resolved | Content swap with no animation — immediate; simple text replacement | — | — |

**Reduced motion:** All Phase 23 entrance animations must respect `prefers-reduced-motion` via `motion-safe:` Tailwind prefix — matching the established pattern from Phase 21/22.

---

## Registry Safety

| Registry | Blocks Used | Safety Gate |
|----------|-------------|-------------|
| shadcn official | All existing Phase 21/22 components (already installed) | not required |
| Third-party | none | not applicable |

**No third-party shadcn registries used in Phase 23.** No new npm packages are required — all new components are built from existing shadcn primitives (`button`, `card`, `textarea`), Lucide icons, and CSS variable tokens already present in the project.

---

## Checker Sign-Off

- [ ] Dimension 1 Copywriting: PASS
- [ ] Dimension 2 Visuals: PASS
- [ ] Dimension 3 Color: PASS
- [ ] Dimension 4 Typography: PASS
- [ ] Dimension 5 Spacing: PASS
- [ ] Dimension 6 Registry Safety: PASS

**Approval:** pending
