Sub-issue of #565 (master: HydraNPS end of experience rating). Full design lives on #565. This issue is Phase 1: the hydranps service.
Scope:
- Extend store.Rating: score_scale, head_id, head_type, venue, body_id, end_reason values, client_submitted_at (all omitempty, no migration)
- Store.UpsertBySessionID with head_id ownership check (409 on foreign head_id)
- Extended createRatingRequest + validation; new write-only submit_token; POST /api/v1/ratings/batch (one load, merge all, save)
- /admin: head/venue/body columns, venue filter, ?limit= for rows only; summary over the full set, split by end_reason: NPS numbers (Rated, AvgScore, RatingRate) over end_reason quit ONLY, Disconnects counted separately, disconnect scores ignored
- Card spec file docs/design/rating-card.md (copy, colors, star SVG from hydraneckwebrtc inject.go, geometry, timings, behavior)
- First _test.go files: scoring, validation, upsert, ownership rejection, summary vs limit split
- ROTATE both tokens leaked in git history of docs/testbooks/nps-e2e.md (already removed from the file in d14b3bc)
- Fix server.listen doc drift
Verify: curl a head shaped record, re-post it, confirm one row; post same session_id with different head_id, confirm 409; confirm disconnect records never move AvgScore.
Blocks: all other #565 sub-issues.