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

# T01: Add GET /admin/pipeline/technique-pages endpoint

Add a new admin endpoint that returns technique pages with aggregated source video counts, version counts, body_sections_format, and creator info. Supports filtering by multi_source_only (boolean) and creator (slug). This provides the data source for the admin technique pages UI.

The endpoint lives in `backend/routers/pipeline.py` alongside other admin endpoints. It queries TechniquePage with joins to Creator, counts from technique_page_videos and technique_page_versions, and returns a paginated response.

## Steps

1. Read `backend/routers/pipeline.py` to understand existing admin endpoint patterns (imports, auth, response style).
2. Read `backend/schemas.py` to see existing schema patterns.
3. Add a new Pydantic response schema `AdminTechniquePageItem` in `backend/schemas.py` with fields: id, title, slug, creator_name, creator_slug, topic_category, body_sections_format, source_video_count (int), version_count (int), created_at, updated_at. Add `AdminTechniquePageListResponse` with items list and total count.
4. Add `GET /admin/pipeline/technique-pages` endpoint in `backend/routers/pipeline.py`. Query uses: `select(TechniquePage)` with `selectinload(TechniquePage.creator)`, correlated subqueries for source_video_count (count from technique_page_videos where technique_page_id matches) and version_count (count from technique_page_versions). Support query params: `multi_source_only: bool = False` (filter source_video_count > 1), `creator: str | None` (filter by creator slug), `sort: str = 'recent'` (recent/alpha/creator), `offset: int = 0`, `limit: int = 50`.
5. Test endpoint manually via curl against the running API on ub01.

## Must-Haves

- [ ] Endpoint returns correct source_video_count and version_count per page
- [ ] multi_source_only=true filters to pages with >1 source video
- [ ] creator filter works by slug
- [ ] Response includes body_sections_format field
- [ ] Pagination works (offset/limit/total)

## Verification

- `ssh ub01 'cd /vmPool/r/repos/xpltdco/chrysopedia && PYTHONPATH=backend python -c "from schemas import AdminTechniquePageItem, AdminTechniquePageListResponse; print(AdminTechniquePageItem.model_fields.keys())"'` exits 0
- After deploying: `curl -s http://ub01:8096/api/v1/admin/pipeline/technique-pages | python3 -m json.tool | head -30` returns valid JSON with items array containing source_video_count and version_count fields

## Inputs

- ``backend/routers/pipeline.py` — existing admin endpoint patterns`
- ``backend/schemas.py` — existing Pydantic schema patterns`
- ``backend/models.py` — TechniquePage, TechniquePageVideo, TechniquePageVersion models`

## Expected Output

- ``backend/routers/pipeline.py` — new GET /admin/pipeline/technique-pages endpoint`
- ``backend/schemas.py` — new AdminTechniquePageItem and AdminTechniquePageListResponse schemas`

## Verification

ssh ub01 'cd /vmPool/r/repos/xpltdco/chrysopedia && PYTHONPATH=backend python -c "from schemas import AdminTechniquePageItem; print(AdminTechniquePageItem.model_fields.keys())"' && curl -sf http://ub01:8096/api/v1/admin/pipeline/technique-pages | python3 -c 'import json,sys; d=json.load(sys.stdin); assert "items" in d; print(f"OK: {d["total"]} pages")'
