# Debug Plan: Half Question Button Locks QueryBox
**Date:** 2026-05-18
**Symptom:** After clicking "Half Question", the textarea appears locked (grayed out, cursor-not-allowed), raising concern that incoming ASR text chunks won't accumulate.
---
## Root Cause Analysis
### The Disable Chain
```
User clicks "Half Question"
→ QueryInput.onClick: onHalfQuestion(trimmed)
→ LTTPage.handleHalfQuestion: queryStream.decomposeOnly({ question })
→ queries.tsx decomposeOnly: setState({ phase: 'decomposing' }) [IMMEDIATE]
→ LTTPage: isLoading = true (phase !== 'idle' && !== 'completed' && !== 'error')
→ QueryInput.textarea: disabled={isLoading} = true ← TEXTAREA LOCKS
→ QueryInput.buttons: disabled={isDisabled} = true ← BUTTONS LOCK
```
### What ACTUALLY Happens vs What User Perceives
| Concern | Reality |
|---------|---------|
| Text accumulation stops? | **No.** React controlled `value={displayValue}` updates even when `disabled`. ASR text continues to display. Playwright confirmed: 250→421 chars during Half Question processing. |
| Buttons permanently locked? | **No.** When SSE `completed` event arrives, phase→`completed`, `isLoading`→`false`, all re-enabled. |
| Textarea editable during wait? | **No.** This IS the issue. Textarea is `disabled={isLoading}`, so user cannot edit/select text during the 5-30s LLM decomposition call. |
### The Real Bug
The textarea should NOT be disabled during half-question processing. The half-question feature is meant to be non-blocking — users should be able to:
1. **Continue seeing ASR text accumulate** ✅ (works via controlled component)
2. **Edit the accumulating text** ❌ (textarea disabled prevents typing/selection)
3. **Click "Final Submit" later** ❌ (button disabled during API call)
4. **Click "Half Question" again** ❌ (button disabled during API call)
**But** #2 is the primary concern. Users physically cannot interact with the textarea while the LLM decomposes the question — and they can't even see that text IS still accumulating because the grayed-out disabled style makes it look frozen.
---
## Where Every Prop Wires
```
frontend/src/pages/LTTPage.tsx
line 73: handleHalfQuestion → queryStream.decomposeOnly({ question })
line 81: isLoading = phase !== 'idle' && !== 'completed' && !== 'error'
line 166: ← shared isLoading
frontend/src/components/QueryInput.tsx
line 52: isDisabled = isLoading || displayValue.trim() === ''
line 66: ← THIS locks the textarea
line 81:
line 89:
```
---
## Root Cause Summary
**Line 66 of QueryInput.tsx**: `disabled={isLoading}`
The textarea shares `isLoading` with the buttons. During the API call, `isLoading=true`, which:
- `disabled` attribute on textarea → gray background, no cursor, no user input
- Buttons become disabled via `isDisabled` (line 52)
The textarea **does not need to be disabled** during an API call — the buttons are sufficient to prevent duplicate submissions. The textarea should remain interactive so users can continue seeing and editing the accumulating ASR text.
---
## Fix Plan
### Step 1: Remove `disabled={isLoading}` from textarea (line 66)
Change:
```tsx
disabled={isLoading}
```
To:
```tsx
disabled={false} // or remove the disabled prop entirely
```
The textarea should never be disabled — `isDisabled` on the buttons already prevents submissions when text is empty.
### Step 2: Keep buttons on `isDisabled` (lines 81, 89)
Buttons continue to use `isDisabled = isLoading || displayValue.trim() === ''` — they remain disabled during API calls AND when text is empty.
### Step 3: (Optional) Remove `disabled:bg-gray-100 disabled:cursor-not-allowed` from textarea CSS
Line 55-57 of the `textareaClassName` includes `disabled:bg-gray-100 disabled:cursor-not-allowed` — if the textarea is no longer disabled, these are inert. Can be removed for cleanliness but not required.
### Risk Assessment
| Risk | Severity | Mitigation |
|------|----------|------------|
| User types while LLM uses old text | Low | Half Question already sent with the text at click time. ASR accumulation doesn't invalidate previous half-question results. |
| Double-click Half Question | Low | `isDisabled` on the button prevents this (isLoading=true during call). |
| User edits text then clicks Final Submit | None | This is desired behavior. |
### Test Update
File: `frontend/src/test/test_phase2_QueryInput_integration.test.tsx`
No test currently asserts that the textarea is disabled during `isLoading`. Only tests check button disabled state. If any test relies on textarea being disabled, update it to expect `toBeEnabled()`.
---
## Phase Sequence Timeline (decomposeOnly)
```
t=0ms setState({ phase: 'decomposing' }) → isLoading=true (textarea locked, buttons locked)
t=2s SSE event 'decomposed' → phase='retrieving' (still locked)
t=3s SSE event 'completed' → phase='completed' → isLoading=false (UNLOCKED)
```
The textarea is locked for ~3 seconds (LLM network round trip + decomposition time). This is the window where users see the grayed-out textarea and think text has stopped accumulating.
---
## Verification Checklist
- [ ] Textarea never gets `disabled` attribute, regardless of `isLoading`
- [ ] Textarea remains editable/selectable during API calls
- [ ] ASR partial text continues to display in textarea during API calls
- [ ] Half Question button disabled during API call (prevents double-click)
- [ ] Final Submit button disabled during API call
- [ ] Both buttons re-enable after API call completes
- [ ] Both buttons disabled when textarea is empty
- [ ] `pnpm test` passes
- [ ] Playwright confirms: textarea interactive during ASR streaming + half-question processing