---
phase: 19-agent-inbox
plan: 03
type: execute
wave: 1
depends_on: []
files_modified: [packages/web/src/components/QuestionForm.tsx, packages/web/src/components/OptionGroup.tsx, packages/web/src/components/FreeTextInput.tsx]
autonomous: true
---

<objective>
Build the question form components that render multi-question forms with mixed input types.

Purpose: Agents ask structured questions with options (radio/checkbox), multi-select, free-text, and "Other" fields. These components render the question forms inside the message detail view. Following the wireframe spec from docs/wireframes/agent-inbox.md.

Output: QuestionForm, OptionGroup, and FreeTextInput components.
</objective>

<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md

@docs/wireframes/agent-inbox.md
@src/agent/types.ts
@packages/web/src/components/ui/input.tsx
@packages/web/src/components/ui/label.tsx
@packages/web/src/components/ui/textarea.tsx
@packages/web/src/components/ui/button.tsx
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create OptionGroup and FreeTextInput components</name>
  <files>packages/web/src/components/OptionGroup.tsx, packages/web/src/components/FreeTextInput.tsx</files>
  <action>
**OptionGroup** — renders radio buttons OR checkboxes for a question's options. Props:
- `questionId: string` — for form identification
- `options: Array<{ label: string; description?: string }>` — available choices
- `multiSelect: boolean` — checkboxes (true) or radio buttons (false)
- `value: string` — current selection (for radio: single label, for checkbox: comma-separated labels)
- `onChange: (value: string) => void` — change handler
- `allowOther?: boolean` — show "Other" free-text option (default true)

Behavior:
- Radio buttons for single-select (default). Use native HTML radio inputs with Tailwind styling.
- Checkboxes for multi-select. Use native HTML checkbox inputs with Tailwind styling.
- Each option shows label and optional description in lighter text
- "Other" option: text input that auto-selects its radio/checkbox when user types. The "Other" value is whatever the user typed.
- For multi-select, value is comma-joined selected labels. When "Other" is checked, append the typed text.

**FreeTextInput** — renders when question has NO options (pure free-text). Props:
- `questionId: string`
- `value: string`
- `onChange: (value: string) => void`
- `multiline?: boolean` — textarea vs single input (default false)
- `placeholder?: string`

Use shadcn Input for single-line, shadcn Textarea for multiline. Keep it simple.
  </action>
  <verify>npx tsc --noEmit -p packages/web/tsconfig.app.json</verify>
  <done>
    - OptionGroup renders radio or checkbox based on multiSelect prop
    - "Other" field auto-selects when typed into
    - FreeTextInput renders Input or Textarea based on multiline prop
  </done>
</task>

<task type="auto">
  <name>Task 2: Create QuestionForm component</name>
  <files>packages/web/src/components/QuestionForm.tsx</files>
  <action>
QuestionForm orchestrates rendering multiple questions with mixed input types. Props:
- `questions: Array<{ id: string; question: string; options?: Array<{ label: string; description?: string }>; multiSelect?: boolean }>` — structured question data from agent
- `onSubmit: (answers: Record<string, string>) => void` — submit handler with questionId→answer map
- `onCancel: () => void` — cancel handler
- `isSubmitting?: boolean` — disable form during submission

Internal state:
- `answers: Record<string, string>` — maps question ID to answer string

Behavior:
1. Render each question sequentially with "Q1:", "Q2:", etc. prefix and question text
2. For each question, determine input type:
   - If `options` array exists → render OptionGroup (with multiSelect from question)
   - If no options → render FreeTextInput
3. Track answers in local state via `onAnswerChange` callbacks
4. "Send Answers" button: enabled only when ALL questions have non-empty answers
5. "Cancel" button: always enabled
6. On submit: call `onSubmit(answers)` with the complete answer map
7. Button row at bottom: [Cancel] [Send Answers] — right-aligned, matching wireframe

Use shadcn Button for actions.
  </action>
  <verify>npx tsc --noEmit -p packages/web/tsconfig.app.json</verify>
  <done>
    - QuestionForm renders mixed question types from questions array
    - Submit disabled until all questions answered
    - onSubmit called with Record<string, string> mapping questionId to answer
  </done>
</task>

</tasks>

<verification>
Before declaring plan complete:
- [ ] `npx tsc --noEmit -p packages/web/tsconfig.app.json` passes
- [ ] `npx vite build` in packages/web succeeds
</verification>

<success_criteria>

- OptionGroup handles radio (single-select) and checkbox (multi-select) with "Other" field
- FreeTextInput handles single-line and multiline inputs
- QuestionForm renders sequential questions with correct input type per question
- Submit validates all questions answered
- All builds pass
  </success_criteria>

<output>
After completion, create `.planning/phases/19-agent-inbox/19-03-SUMMARY.md`
</output>
