24 KiB
| phase | slug | status | shadcn_initialized | preset | created |
|---|---|---|---|---|---|
| 23 | brainstormer-flow | draft | true | new-york / neutral / css-variables | 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 withrole === "system"rendering branch for handoff/spec/status-update messagesChatPanel.tsx— extend with Brainstormer default agent selection on new conversationAgentIcon(fromAgentIconPicker.tsx) — used for Brainstormer identity in all message identity barsagentRoleColors(fromagent-role-colors.ts) —generalrole maps totext-slate-600 dark:text-slate-400for Brainstormer avatarissuesApi— referenced fromui/src/api/issuesfor task creation link-out
New DB migration required:
- Add
message_typecolumn tochat_messagestable: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:
- If a
generalrole agent exists in the workspace — select it - If multiple
generalagents exist — select the first bycreatedAtascending - If no
generalagent exists — fall back to the first agent alphabetically (same as current behavior) - Auto-selection fires only when
activeAgentId === nulland 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]— usesbg-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
ChatTaskCreatedBadgeper task, stacked vertically withgap-2wrapper
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):
- Hovered conversation list row
- Currently active/selected conversation row
- Code block toolbar background on hover
- Input focus-within ring
- 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— addsmessage_type textnullable column tochat_messagestable
Icons (lucide-react) — Phase 23 additions:
CheckCircle2— task completion status badgeBrain— recommended default icon for the Brainstormer agent (user configures, butbrainis a validAGENT_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:
ChatHandoffIndicatorappended immediately before API call completes; spec card buttons disabled. On API failure,ChatHandoffIndicatoris removed and buttons re-enabled with failure toast. - Task creation:
ChatTaskCreatedBadgeappears with "Creating task..." state; updates in place whentaskIdresolves.
Theme Integration Contract
Phase 23 extends Phase 21/22's zero-new-plumbing approach:
- Spec card uses
bg-cardandborder-border— resolves correctly in all three themes via CSS variables - Task/status badges use
bg-cardandborder-border— same - Handoff separator uses
border-borderfor the<hr>lines — correct in all themes CheckCircle2completion icon usestext-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 variablesChatHandoffIndicator:text-muted-foreground,border-border— CSS variablesChatTaskCreatedBadge:bg-card,border-border,text-foreground,text-primary— CSS variablesChatStatusUpdateBadge: 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