# S02: Pipeline Page: Head/Tail Log View + Token Count

**Goal:** Pipeline event log supports Head (chronological) and Tail (most recent) viewing modes with a toggle UI, plus the backend `order` query parameter that drives it.
**Demo:** After this: Pipeline event log shows Head/Tail toggle buttons. Head shows first N events, Tail shows last N events. Token counts visible per event and per video.

## Tasks
- [x] **T01: Added `order` query parameter (asc/desc, default desc) to pipeline events endpoint with validation and dynamic ordering** — Add an `order` query parameter (values: `asc`/`desc`, default `desc`) to the `GET /admin/pipeline/events/{video_id}` endpoint in `backend/routers/pipeline.py` on ub01. Validate the value and apply `PipelineEvent.created_at.asc()` or `.desc()` accordingly. This preserves the existing default behavior (desc) while enabling chronological viewing.

## Steps

1. SSH to ub01, read the current `list_pipeline_events` function in `/vmPool/r/repos/xpltdco/chrysopedia/backend/routers/pipeline.py` (around line 179-225)
2. Add `order: Annotated[str | None, Query(description="Sort order: asc or desc")] = "desc"` parameter to the function signature
3. Add validation: if `order` not in `("asc", "desc")`, raise `HTTPException(400, "order must be 'asc' or 'desc'")`
4. Change the hardcoded `.order_by(PipelineEvent.created_at.desc())` to use `PipelineEvent.created_at.asc() if order == "asc" else PipelineEvent.created_at.desc()`
5. Rebuild and restart the API container: `cd /vmPool/r/repos/xpltdco/chrysopedia && docker compose build chrysopedia-api && docker compose up -d chrysopedia-api`
6. Verify with curl: `curl 'http://ub01:8096/api/v1/admin/pipeline/events/{video_id}?order=asc&limit=3'` should return events in ascending chronological order. Compare timestamps with `?order=desc&limit=3` to confirm different ordering.

## Must-Haves

- [x] `order` param accepts `asc` and `desc`
- [x] Default is `desc` (preserves existing behavior)
- [x] Invalid values return 400
- [x] `order=asc` returns events oldest-first

## Negative Tests

- `?order=invalid` → 400 error
- No `order` param → same as `desc` (backward compatible)

## Verification

- `curl -s 'http://ub01:8096/api/v1/admin/pipeline/events/{video_id}?order=asc&limit=3' | python3 -m json.tool` returns events with ascending timestamps
- `curl -s 'http://ub01:8096/api/v1/admin/pipeline/events/{video_id}?order=invalid'` returns 400
  - Estimate: 20m
  - Files: backend/routers/pipeline.py (on ub01: /vmPool/r/repos/xpltdco/chrysopedia/backend/routers/pipeline.py)
  - Verify: curl -s 'http://ub01:8096/api/v1/admin/pipeline/events/{video_id}?order=asc&limit=3' returns events in ascending timestamp order; curl with order=invalid returns HTTP 400
- [x] **T02: Added Head/Tail segmented toggle to EventLog with order param wiring — Head shows oldest-first (asc), Tail shows newest-first (desc), switching resets pagination** — Add a Head/Tail segmented toggle to the EventLog component in AdminPipeline.tsx. Wire it to pass the `order` param through the API client. Add compact segmented-button CSS.

## Steps

1. Read `frontend/src/api/public-client.ts` around line 440. Add `order?: "asc" | "desc"` to the `fetchPipelineEvents` params type. Add `if (params.order) qs.set("order", params.order);` to the URL builder.
2. Read `frontend/src/pages/AdminPipeline.tsx`. In the `EventLog` component:
   - Add `viewMode` state: `const [viewMode, setViewMode] = useState<"head" | "tail">("tail");`
   - Pass `order: viewMode === "head" ? "asc" : "desc"` to `fetchPipelineEvents` in the `load` callback
   - Add `viewMode` to the `useCallback` dependency array
   - Add a mode-switch handler that sets viewMode and resets offset to 0
   - Insert the toggle buttons in `.pipeline-events__header` between the count and the refresh button
3. Read `frontend/src/App.css` around the `.pipeline-events__header` section (~line 2735). Add CSS for `.pipeline-events__view-toggle` (inline-flex container with shared border-radius) and `.pipeline-events__view-btn` / `.pipeline-events__view-btn--active` (segmented button pair using existing color variables).
4. Run `cd frontend && npm run build` to verify zero TypeScript errors.
5. Verify in browser at http://ub01:8096 — navigate to Admin > Pipeline, expand a video, confirm Head/Tail toggle appears and switches event ordering. Confirm token counts still display on each event row and video summary.

## Must-Haves

- [x] `viewMode` state with `head`/`tail` values, default `tail`
- [x] Toggle buttons render in event log header
- [x] Head mode passes `order=asc`, Tail passes `order=desc`
- [x] Switching modes resets offset to 0
- [x] Existing prev/next pager still works within each mode
- [x] Token counts per-event and per-video remain visible and unchanged
- [x] Segmented button CSS uses existing CSS custom properties

## Verification

- `cd frontend && npm run build` exits 0
- Browser: Head button shows oldest events first, Tail shows newest first
- Browser: Token counts visible on event rows and video summary rows
  - Estimate: 30m
  - Files: frontend/src/api/public-client.ts, frontend/src/pages/AdminPipeline.tsx, frontend/src/App.css
  - Verify: cd frontend && npm run build exits 0 with no TypeScript errors
