Files
brain-of-reese/.agent/user_stories/suggestion-chips.md
T
ducoterra 022da8e2bc feat: scaffold Brain of Reese — FastAPI RAG chat over Postgres 17 + pgvector
Foundation (phase 01, verified):
- FastAPI app: /api/health, /api/suggestions, /api/chat (placeholder),
  static frontend served locally (no CDN)
- Postgres 17 + pgvector via db/Containerfile + compose.yaml
  (podman compose up -d db), Alembic initial migration (documents,
  chunks with vector(768), query_log)
- LLM client targeting https://aipi.reeseapps.com/v1 (turbo/embed);
  scripts/llm_probe.py verified models + 768-dim embeddings live
- Conditional debugpy: imported only when DEBUGPY=1 (attach on demand,
  :5678); logging config for clean single-line logs
- Frontend shell: mobile-first chat + Sources pages, tokens, a11y baselines
- Tests: 24 unit+integration (99% coverage on app/), ruff + pyright clean,
  Playwright smoke E2E (3 tests) against a deterministic mock LLM
- Planning: .agent/PLAN.md (architecture + LOCKED decisions), AGENTS.md,
  6 user stories, 7 phase files (one story / one phase / one Playwright
  suite each)
2026-08-21 13:42:21 -04:00

59 lines
2.8 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.
# Story: Suggestion Chips
**Phase:** `05_story_suggestion_chips.md` · **E2E:** `tests/e2e/test_suggestion_chips.py`
## Narrative
As **a user who opens the chat for the first time** (or after a deflection),
I want a few **concrete example questions** right in front of me — so I
immediately understand what Brain is good at and can start with zero
friction.
- **Given** I land on the chat page
- **When** the app is healthy
- **Then** I see 3–4 suggestion chips drawn from `GET /api/suggestions`
(defaults in settings, tuned to the real Homelab topics), and clicking one
fills the composer and submits it.
## Acceptance criteria
1. `GET /api/suggestions` returns the configured list (settings-driven,
overridable via `BOR_SUGGESTIONS` JSON env).
2. Chips render in the empty state as `<button class="suggestion-chip">`
(real buttons, not links/divs) with `role="list"` container +
`role="listitem"` items; `aria-label="Suggested questions"` on the group.
3. Click behavior: fills `#message-input`, focuses it, **and submits**
(one tap → answer). Keyboard: Tab to chip, Enter activates.
4. After the first user message the empty state (and its chips) is replaced
by the conversation; chips re-appear only on deflection (see
honest-deflection story).
5. If `/api/suggestions` fails, the chat still works (progressive
enhancement — no chips, no error spam).
6. Mobile: chips become a horizontally scrollable single row
(no wrapping into the composer's territory).
## UI Visualization & Structure
- Chips: pill (`border-radius: 999px`), `bg --brand-soft`, `text --brand-ink`
(≥6:1), 1px `--line` border, **min-height 44px**, comfortable
`padding 0.55rem 1rem`; hover deepens bg; `:active` scales 0.98.
- Desktop: `flex-wrap: wrap`, centered under the empty-state subcopy, gap 0.5rem.
- Mobile (≤640px): `flex-wrap: nowrap; overflow-x: auto` single row,
`scrollbar-width: thin`, chips `flex: 0 0 auto` (thumb-friendly, no
accidental double-tap on wrapped lines).
- Default suggestion copy (tune to real docs in this phase):
1. "How is my Kubernetes cluster set up?"
2. "What's my backup strategy?"
3. "How do I deploy a new service?"
4. "What's currently running in the homelab?"
## Playwright Mapping Rule
**Test Scenario → `tests/e2e/test_suggestion_chips.py`**:
1. `test_onboarding_chips_render` — goto `/`, assert ≥3 `.suggestion-chip`
visible inside `#suggestions` (role=list) with non-empty text.
2. `test_chip_click_submits` — click the first chip; assert a user bubble
with the chip's exact text appears and the brain reply (mock) follows.
3. `test_chips_keyboard_accessible` — Tab from the page start reaches the
first chip; Enter submits it.
4. `test_chips_mobile_row` — at 375px viewport, the chip row is
horizontally scrollable (`scrollWidth > clientWidth` or single-line
height check) and no chip is cut vertically.