feat: quote board & session highlights (#116) #194

Merged
claude-bot merged 2 commits from feat/116-session-highlights into main 2026-07-18 02:03:00 +00:00
Contributor

Closes #116. Third feature of v3.7.0 Table Tools.

Extracts memorable quotes + key moments during audio processing and surfaces them as approval-gated highlights, plus a campaign-wide quote board.

Backend + bot (86e6e66)

  • New SessionHighlight model (quote|moment, speaker, text, timestamp_ref, approved, edited_by) + migration b4c5d6e7f8a9 (round-trips). Campaign gains highlights_max_quotes (5) + highlights_in_discord (true).
  • extract_highlights LLM pass hooked into process_audio: verbatim-quote prompt, defensive JSON parsing, and a hallucination filter — a quote is dropped unless it substring-matches the diarized transcript (whitespace/case-normalised). Never raises; the pipeline hook is non-fatal (a highlight failure can't fail audio processing or change status) and idempotent (delete-then-reinsert).
  • Endpoints: GET/POST/PATCH/DELETE /sessions/{id}/highlights (GM full CRUD; members see only approved, GM sees drafts too), GET /campaigns/{id}/highlights?speaker= quote board (approved quotes across sessions, newest first). approve_audio flips the session's highlights to approved.
  • Bot: when highlights_in_discord is on, the session_summary_approved payload carries an optional top_quote; the bot adds a "💬 Quote of the session" embed field. Absent/off → embed unchanged. Additive → no BOT_CONTRACT_VERSION bump.

Frontend (<head>)

  • SessionHighlights panel on the session page: quotes/moments, GM add/edit/delete + a "Draft — not yet visible to players" badge on unapproved rows; members see approved-only, read-only (relies on the server's row filter).
  • CampaignHighlights quote-board page at /campaigns/:id/highlights with a speaker filter + session links, and a "Quote Board" nav link.
  • Campaign settings: highlights_max_quotes + highlights_in_discord.

Tests

  • Backend: +12 in test_highlights.py — 6 extraction/validator (hallucination filter, JSON-parse resilience, cap) + 6 API/integration (GM CRUD, GM-vs-member visibility gate, approve-flips-to-visible, quote board + speaker filter). Full suite 478 pass; migration round-trip verified.
  • Frontend: +23 (highlights api 8, SessionHighlights 11, CampaignHighlights 4). 229 pass, eslint clean, vite build green.

Notes

  • The extraction step is deliberately best-effort and isolated: transcript/quote text is never logged (counts only), and a bad LLM response yields zero highlights rather than a failed session.
  • The 6 API tests were added on top of the delegate's validator unit tests to cover the acceptance-criteria behaviors (visibility gate, approval flip, quote board) that the unit tests didn't reach.

🤖 Generated with Claude Code

Closes #116. Third feature of **v3.7.0 Table Tools**. Extracts memorable quotes + key moments during audio processing and surfaces them as approval-gated highlights, plus a campaign-wide quote board. ## Backend + bot (`86e6e66`) - New `SessionHighlight` model (`quote`|`moment`, speaker, text, timestamp_ref, approved, edited_by) + migration `b4c5d6e7f8a9` (round-trips). Campaign gains `highlights_max_quotes` (5) + `highlights_in_discord` (true). - `extract_highlights` LLM pass hooked into `process_audio`: verbatim-quote prompt, defensive JSON parsing, and a **hallucination filter** — a quote is dropped unless it substring-matches the diarized transcript (whitespace/case-normalised). **Never raises**; the pipeline hook is **non-fatal** (a highlight failure can't fail audio processing or change status) and **idempotent** (delete-then-reinsert). - Endpoints: `GET/POST/PATCH/DELETE /sessions/{id}/highlights` (GM full CRUD; **members see only `approved`**, GM sees drafts too), `GET /campaigns/{id}/highlights?speaker=` quote board (approved quotes across sessions, newest first). `approve_audio` flips the session's highlights to approved. - Bot: when `highlights_in_discord` is on, the `session_summary_approved` payload carries an optional `top_quote`; the bot adds a "💬 Quote of the session" embed field. Absent/off → embed unchanged. **Additive → no `BOT_CONTRACT_VERSION` bump.** ## Frontend (`<head>`) - `SessionHighlights` panel on the session page: quotes/moments, GM add/edit/delete + a "Draft — not yet visible to players" badge on unapproved rows; members see approved-only, read-only (relies on the server's row filter). - `CampaignHighlights` quote-board page at `/campaigns/:id/highlights` with a speaker filter + session links, and a "Quote Board" nav link. - Campaign settings: `highlights_max_quotes` + `highlights_in_discord`. ## Tests - Backend: +12 in `test_highlights.py` — 6 extraction/validator (hallucination filter, JSON-parse resilience, cap) + **6 API/integration** (GM CRUD, GM-vs-member visibility gate, approve-flips-to-visible, quote board + speaker filter). Full suite **478 pass**; migration round-trip verified. - Frontend: +23 (highlights api 8, SessionHighlights 11, CampaignHighlights 4). **229 pass**, eslint clean, vite build green. ## Notes - The extraction step is deliberately best-effort and isolated: transcript/quote text is never logged (counts only), and a bad LLM response yields zero highlights rather than a failed session. - The 6 API tests were added on top of the delegate's validator unit tests to cover the acceptance-criteria behaviors (visibility gate, approval flip, quote board) that the unit tests didn't reach. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Extracts memorable quotes + key moments during audio processing and
surfaces them as approval-gated highlights.

Backend:
- New SessionHighlight model (quote|moment, speaker, text, timestamp_ref,
  approved, edited_by) + HighlightKind enum; migration b4c5d6e7f8a9
  (round-trips). Campaign gains highlights_max_quotes (5) and
  highlights_in_discord (true) settings.
- audio_service.extract_highlights: LLM pass via generate_structured_text
  (json_mode) with a verbatim-quote prompt, defensive JSON parsing, and a
  hallucination filter — a quote is dropped unless it substring-matches the
  diarized transcript (whitespace/case-normalised). Never raises.
- process_audio hook: best-effort (a highlight failure never fails audio
  processing or changes status) and idempotent (delete-then-reinsert).
- Endpoints: GET/POST/PATCH/DELETE /sessions/{id}/highlights (GM full;
  members see approved only), GET /campaigns/{id}/highlights quote board
  (approved quotes across sessions, ?speaker= filter). approve_audio flips
  the session's highlights to approved.

Bot (additive — no BOT_CONTRACT_VERSION bump):
- When highlights_in_discord is on, the session_summary_approved payload
  carries an optional top_quote; the bot adds a "Quote of the session"
  embed field. Absent/off → embed unchanged.

Tests: +12 in test_highlights.py — 6 extraction/validator (hallucination
filter, JSON-parse resilience, cap) + 6 API (GM CRUD, GM-vs-member
visibility gate, approve flips to visible, quote board + speaker filter).
Full backend suite 478 pass; migration round-trip verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(frontend): session highlights panel + campaign quote board (#116)
All checks were successful
CI / Docker image build (pull_request) Successful in 23s
CI / Backend lint (ruff) (pull_request) Successful in 59s
CI / Frontend tests, audit, and build (pull_request) Successful in 1m54s
CI / Bot tests and audit (pull_request) Successful in 2m18s
CI / Backend migration, tests, and audit (pull_request) Successful in 4m7s
8223efd4db
- api/highlights.js: session highlight CRUD + campaign quote board reads
  (?speaker= built only when provided).
- SessionHighlights component (SessionDetail): renders quotes/moments; GM
  gets add/edit/delete + a "Draft — not yet visible to players" badge on
  unapproved rows; members see only what the backend returns (approved),
  read-only. Relies on the server's row-level visibility filter, not
  client-side approval filtering.
- CampaignHighlights page (/campaigns/:id/highlights): the quote board —
  approved quotes newest-first with a speaker filter and session links; a
  "Quote Board" nav link on the campaign page (all members).
- CampaignDetail settings: highlights_max_quotes (0–20) + highlights_in_discord
  toggle wired into the campaign PATCH (clamped, never null).

Tests: +23 (highlights api 8, SessionHighlights 11, CampaignHighlights 4).
229 pass, eslint clean, vite build succeeds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot deleted branch feat/116-session-highlights 2026-07-18 02:03:00 +00:00
Sign in to join this conversation.
No description provided.