---
estimated_steps: 26
estimated_files: 4
skills_used: []
---

# T02: Add random technique endpoint and discovery button on homepage

## Description

Vertical feature for R018: backend endpoint, API client function, and frontend button with navigation. The existing `GET /api/v1/techniques?sort=random&limit=1` works but returns the full paginated list response. A dedicated `GET /api/v1/techniques/random` returning just `{ slug: string }` is cleaner.

## Failure Modes

| Dependency | On error | On timeout | On malformed response |
|------------|----------|-----------|----------------------|
| `GET /techniques/random` | Button shows brief error state, stays on page | Same as error — fetch has default timeout | Shouldn't happen (typed response), but guard with optional chaining |
| No techniques in DB | Endpoint returns 404 | N/A | Button hidden or disabled |

## Steps

1. **Backend endpoint** — In `backend/routers/techniques.py`, add `GET /random` endpoint BEFORE the `/{slug}` route (line ~185) to avoid slug capture. The endpoint queries `SELECT slug FROM technique_pages ORDER BY random() LIMIT 1`, returns `{"slug": "..."}`. If no techniques exist, return 404.

2. **API client function** — In `frontend/src/api/public-client.ts`, add `fetchRandomTechnique(): Promise<{ slug: string }>` that calls `GET /api/v1/techniques/random`.

3. **Random button in Home.tsx** — Add a "Random Technique" button after the nav-cards section (or near the CTA area). Use a dice/shuffle icon (SVG inline or emoji 🎲). On click: set loading state, call `fetchRandomTechnique()`, then `navigate(`/techniques/${slug}`)`. If fetch fails, briefly show error then reset. The button should have the `.btn` base class with a secondary style.

4. **Button CSS** — Add `.btn--random` styles in `App.css` if needed (may just use existing `.btn` with minor tweaks). Consider a subtle shuffle icon animation on hover.

## Must-Haves

- [ ] `GET /api/v1/techniques/random` returns `{ slug: string }` with 200, or 404 if empty
- [ ] Route registered before `/{slug}` to avoid capture
- [ ] `fetchRandomTechnique()` in public-client.ts
- [ ] Button visible on homepage, navigates to a technique on click
- [ ] Button has loading state during fetch
- [ ] `npx tsc --noEmit` passes
- [ ] `npm run build` succeeds

## Verification

- `cd frontend && npx tsc --noEmit` — no type errors
- `cd frontend && npm run build` — Vite build succeeds
- `grep -q 'fetchRandomTechnique' frontend/src/api/public-client.ts` — client function exists
- `grep -q '/random' backend/routers/techniques.py` — endpoint exists
- `grep -q 'Random' frontend/src/pages/Home.tsx` — button exists in homepage

## Inputs

- ``backend/routers/techniques.py` — existing techniques router with `sort=random` logic at line 158`
- ``frontend/src/api/public-client.ts` — existing API client with `fetchTechniques()``
- ``frontend/src/pages/Home.tsx` — homepage component (T01 output with stagger indices)`
- ``frontend/src/App.css` — styles (T01 output with card animations)`

## Expected Output

- ``backend/routers/techniques.py` — new GET /random endpoint before /{slug}`
- ``frontend/src/api/public-client.ts` — new fetchRandomTechnique() function`
- ``frontend/src/pages/Home.tsx` — Random Technique button with navigation logic`
- ``frontend/src/App.css` — random button styling if needed`

## Verification

cd frontend && npx tsc --noEmit && npm run build && grep -q 'fetchRandomTechnique' src/api/public-client.ts && grep -q '/random' ../backend/routers/techniques.py
