---
estimated_steps: 17
estimated_files: 5
skills_used: []
---

# T01: Add key_moment_count to technique list API and render topic tags + moment count on homepage cards

Two coupled changes: (1) Backend — add `key_moment_count: int = 0` field to `TechniquePageRead` schema and populate it via a correlated COUNT subquery in the `list_techniques` endpoint, matching the existing pattern in `creators.py`. (2) Frontend — add `key_moment_count: number` to the `TechniqueListItem` TypeScript interface, then render `topic_tags` as pill badges and `key_moment_count` as a small inline label in the homepage "Recently Added" cards.

Backend steps:
1. In `backend/schemas.py`, add `key_moment_count: int = 0` to `TechniquePageRead` class
2. In `backend/routers/techniques.py` `list_techniques`, add a correlated subquery: `key_moment_count_sq = select(func.count()).where(KeyMoment.technique_page_id == TechniquePage.id).correlate(TechniquePage).scalar_subquery()` — import KeyMoment from models
3. Add `.add_columns(key_moment_count_sq.label('key_moment_count'))` to the main query
4. Update the result processing loop to set `item.key_moment_count` from the subquery column
5. Rebuild and restart the API container on ub01: `ssh ub01 'cd /vmPool/r/repos/xpltdco/chrysopedia && git pull && docker compose build chrysopedia-api && docker compose up -d chrysopedia-api'`
6. Verify: `curl http://ub01:8096/api/v1/techniques?limit=3 | python3 -m json.tool | grep key_moment_count`

Frontend steps:
1. In `frontend/src/api/public-client.ts`, add `key_moment_count: number` to `TechniqueListItem` interface
2. In `frontend/src/pages/Home.tsx`, inside the `recent-card__meta` span:
   - After the category badge, render `topic_tags` as pills: `{t.topic_tags && t.topic_tags.length > 0 && t.topic_tags.map(tag => <span key={tag} className="pill">{tag}</span>)}`
   - After the summary, render key moment count: `{t.key_moment_count > 0 && <span className="recent-card__moments">{t.key_moment_count} moment{t.key_moment_count !== 1 ? 's' : ''}</span>}`
3. In `frontend/src/App.css`, add minimal styles for `.recent-card__moments` (small, muted text)
4. Run `npx tsc --noEmit` and `npm run build` to verify
5. Rebuild web container: `ssh ub01 'cd /vmPool/r/repos/xpltdco/chrysopedia && docker compose build chrysopedia-web-8096 && docker compose up -d chrysopedia-web-8096'`

Note on the subquery approach: The `list_techniques` endpoint currently uses `select(TechniquePage)` and then `.options(selectinload(...))`. Adding `.add_columns()` changes the result shape from scalar `TechniquePage` objects to `Row` tuples of `(TechniquePage, int)`. The loop that processes results needs to unpack accordingly: `for row in result: p = row[0]; count = row[1]`. Check the existing pattern in `routers/creators.py` lines 37-65 for reference.

## Inputs

- ``backend/schemas.py` — TechniquePageRead class to extend with key_moment_count field`
- ``backend/routers/techniques.py` — list_techniques endpoint to add count subquery`
- ``backend/routers/creators.py` — reference for existing subquery pattern (read-only)`
- ``frontend/src/api/public-client.ts` — TechniqueListItem interface to extend`
- ``frontend/src/pages/Home.tsx` — recent card rendering to add tags and count`
- ``frontend/src/App.css` — styles for new card elements`

## Expected Output

- ``backend/schemas.py` — TechniquePageRead with key_moment_count field`
- ``backend/routers/techniques.py` — list_techniques with key_moment count subquery`
- ``frontend/src/api/public-client.ts` — TechniqueListItem with key_moment_count`
- ``frontend/src/pages/Home.tsx` — recent cards with topic_tags pills and moment count`
- ``frontend/src/App.css` — .recent-card__moments styling`

## Verification

curl -s http://ub01:8096/api/v1/techniques?limit=3 | python3 -c "import sys,json; items=json.load(sys.stdin)['items']; assert all('key_moment_count' in i for i in items), 'missing key_moment_count'; print('API OK')" && cd frontend && npx tsc --noEmit && npm run build
