nexus/.planning/milestones/v1.3-phases/23-brainstormer-flow/23-UI-SPEC.md
Nexus Dev ffc7b130e4 chore: archive v1.3 phase directories to milestones/
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 03:55:48 +00:00

422 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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