@@ -714,11 +714,23 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
714714 }
715715 if m.session != nil && msg.Payload.ID == m.session.ID {
716716 prevHasInProgress := hasInProgressTodo(m.session.Todos)
717+ prevPillsHeight := m.pillsAreaHeight()
717718 m.session = &msg.Payload
718719 if !prevHasInProgress && hasInProgressTodo(m.session.Todos) {
719720 m.todoIsSpinning = true
720721 cmds = append(cmds, m.todoSpinner.Tick)
722+ }
723+ // The pills panel reserves vertical space that the chat area
724+ // must yield. Recompute the layout whenever that footprint
725+ // changes (todos appearing, the list growing, etc.) so the
726+ // box renders on first paint rather than waiting for a toggle.
727+ // When the footprint is unchanged we still re-render the pill
728+ // content so status changes (e.g. the in-progress spinner)
729+ // show up.
730+ if m.pillsAreaHeight() != prevPillsHeight {
721731 m.updateLayoutAndSize()
732+ } else {
733+ m.renderPills()
722734 }
723735 m.autoExpandPillsIfReasonable()
724736 }
@@ -2377,6 +2389,13 @@ func (m *UI) Draw(scr uv.Screen, area uv.Rectangle) *tea.Cursor {
23772389 if m.layout != layout {
23782390 m.layout = layout
23792391 m.updateSize()
2392+ } else if m.state == uiChat && m.hasSession() {
2393+ // Re-render pills on every draw so the box appears even when
2394+ // the layout footprint hasn't changed (e.g. todos arrived
2395+ // while the panel was collapsed). updateSize already calls
2396+ // renderPills, but only when the layout actually differs;
2397+ // this catches the steady-state case.
2398+ m.renderPills()
23802399 }
23812400
23822401 // Clear the screen first