[Table Tools] Player-visible quest log #115

Closed
opened 2026-07-14 19:51:55 +00:00 by claude-bot · 1 comment
Contributor

Context / Motivation

GM-side plot threads exist in the planning workspace: PlotThread (webapp/backend/app/models/plot_thread.py) with title, notes, resolved bool, introduced_session_id, resolved_session_id, linked_lore_entry_id; CRUD lives inline in webapp/backend/app/routers/campaigns.py:3322-3390 (GET/POST /{campaign_id}/planning/threads, PATCH/DELETE .../threads/{thread_id}); sibling CampaignArc model has a real status enum (planned/active/concluded). The player-facing half — "active quests, leads, promises" — is what groups keep in a shared doc today.

Spec

Model choice — extend PlotThread, don't add a parallel model. It already carries session links and a lore link, which is exactly the quest shape; a parallel Quest model would immediately drift. Migration adds:

  • status: enum rumored | active | completed | failed | abandoned (replaces the semantics of resolved; migrate resolved=Truecompleted, Falseactive; keep or drop the bool per migration reviewer's call — document it)
  • player_visible: bool, default false (existing threads stay GM-private)
  • player_summary: nullable Text — the player-facing description; notes remains GM-private always
  • (existing introduced_session_id / resolved_session_id / linked_lore_entry_id cover the linking requirements)

API:

  • Extend the existing planning-thread endpoints (campaigns.py:3322-3390) with the new fields (GM-only, as today).
  • New player-readable endpoint GET /campaigns/{campaign_id}/quests (any member): returns only player_visible=true threads and only public fields (title, status, player_summary, session/lore links) — the serializer must never include notes. Do not reuse the GM ThreadResponse schema; define a separate QuestResponse.

UI:

  • GM: per-thread visibility toggle + status select + player-summary field in the planning workspace.
  • Players: read-only "Quests" section on the campaign page grouped by status (active first, then rumored; completed/failed/abandoned collapsed); optionally a compact list in the wiki sidebar.

Bot (small): extend /next or add /quests in bot/questboard_bot/cogs/sessions.py (command group precedent at :42 for /next) listing active player-visible quests via a new backend endpoint on the bot contract (webapp/backend/app/routers/bot.py + bot/questboard_bot/api_client.py, same commit).

Out of scope (flagged follow-up)

  • LLM-proposed quest status updates from transcripts (belongs with the lore-proposal pipeline later).
  • Player-editable quests or player suggestions.
  • Notifications on quest status changes.

Acceptance criteria

  • API test: a player-role token fetching /quests receives visible quests without notes in any response shape; GM notes never leak (assert on raw JSON keys).
  • GM CRUD covers status + visibility + player summary; toggling visibility immediately affects the player view.
  • Player view renders all five statuses and links to sessions/lore entries.
  • Bot command lists active quests and handles the zero-quest case gracefully.

References

  • webapp/backend/app/models/plot_thread.py (current columns incl. resolved at line 22, session/lore FKs at 23-31)
  • webapp/backend/app/models/campaign_arc.py (ArcStatus enum precedent)
  • webapp/backend/app/routers/campaigns.py:3322-3390 (thread endpoints, ThreadResponse)
  • bot/questboard_bot/cogs/sessions.py:42 (/next)
  • webapp/backend/app/routers/bot.py, bot/questboard_bot/api_client.py (contract pair)

Filed from the July 2026 full-project review.

## Context / Motivation GM-side plot threads exist in the planning workspace: `PlotThread` (`webapp/backend/app/models/plot_thread.py`) with `title`, `notes`, `resolved` bool, `introduced_session_id`, `resolved_session_id`, `linked_lore_entry_id`; CRUD lives inline in `webapp/backend/app/routers/campaigns.py:3322-3390` (`GET/POST /{campaign_id}/planning/threads`, `PATCH/DELETE .../threads/{thread_id}`); sibling `CampaignArc` model has a real status enum (`planned/active/concluded`). The player-facing half — "active quests, leads, promises" — is what groups keep in a shared doc today. ## Spec **Model choice — extend `PlotThread`, don't add a parallel model.** It already carries session links and a lore link, which is exactly the quest shape; a parallel `Quest` model would immediately drift. Migration adds: - `status`: enum `rumored | active | completed | failed | abandoned` (replaces the semantics of `resolved`; migrate `resolved=True` → `completed`, `False` → `active`; keep or drop the bool per migration reviewer's call — document it) - `player_visible`: bool, default `false` (existing threads stay GM-private) - `player_summary`: nullable Text — the player-facing description; **`notes` remains GM-private always** - (existing `introduced_session_id` / `resolved_session_id` / `linked_lore_entry_id` cover the linking requirements) **API**: - Extend the existing planning-thread endpoints (`campaigns.py:3322-3390`) with the new fields (GM-only, as today). - New player-readable endpoint `GET /campaigns/{campaign_id}/quests` (any member): returns only `player_visible=true` threads and only public fields (`title`, `status`, `player_summary`, session/lore links) — the serializer must never include `notes`. Do not reuse the GM `ThreadResponse` schema; define a separate `QuestResponse`. **UI**: - GM: per-thread visibility toggle + status select + player-summary field in the planning workspace. - Players: read-only "Quests" section on the campaign page grouped by status (active first, then rumored; completed/failed/abandoned collapsed); optionally a compact list in the wiki sidebar. **Bot** (small): extend `/next` or add `/quests` in `bot/questboard_bot/cogs/sessions.py` (command group precedent at `:42` for `/next`) listing active player-visible quests via a new backend endpoint on the bot contract (`webapp/backend/app/routers/bot.py` + `bot/questboard_bot/api_client.py`, same commit). ## Out of scope (flagged follow-up) - LLM-proposed quest status updates from transcripts (belongs with the lore-proposal pipeline later). - Player-editable quests or player suggestions. - Notifications on quest status changes. ## Acceptance criteria - API test: a player-role token fetching `/quests` receives visible quests without `notes` in any response shape; GM notes never leak (assert on raw JSON keys). - GM CRUD covers status + visibility + player summary; toggling visibility immediately affects the player view. - Player view renders all five statuses and links to sessions/lore entries. - Bot command lists active quests and handles the zero-quest case gracefully. ## References - `webapp/backend/app/models/plot_thread.py` (current columns incl. `resolved` at line 22, session/lore FKs at 23-31) - `webapp/backend/app/models/campaign_arc.py` (`ArcStatus` enum precedent) - `webapp/backend/app/routers/campaigns.py:3322-3390` (thread endpoints, `ThreadResponse`) - `bot/questboard_bot/cogs/sessions.py:42` (`/next`) - `webapp/backend/app/routers/bot.py`, `bot/questboard_bot/api_client.py` (contract pair) _Filed from the July 2026 full-project review._
Author
Contributor

Done — merged in PR #193 (backend+bot 6d4a0d1, frontend ba5a714). CI green.

Shipped:

  • Extended PlotThread (no parallel model): new QuestStatus enum (rumored/active/completed/failed/abandoned), player_visible, player_summary; dropped resolved, data-migrated resolved=true → completed (migration f2a3b4c5d6e7, upgrade/downgrade/upgrade verified on a reset test DB).
  • GM thread endpoints carry the new fields; notes stays GM-only. New member-readable GET /campaigns/{id}/quests with a separate QuestResponse built by explicit field mapping — structurally can't leak notes.
  • Bot: GET /api/bot/guilds/{guild_id}/quests + /quests slash command (active + player-visible only). Additive → no BOT_CONTRACT_VERSION bump.
  • Frontend: ThreadTracker migrated resolvedstatus with a per-row visibility toggle + player-summary field; new read-only QuestLog on the campaign page (grouped Active/Rumored, resolved collapsed), never shows notes.

Tests: backend 466 pass (+10 test_quests.py, incl. a raw-JSON-keys check that notes never reaches players/bot); frontend 206 (+4 QuestLog).

Acceptance criteria all met: player token gets visible quests without notes; GM CRUD covers status/visibility/summary and toggling visibility immediately affects the player view; player view renders all five statuses with session/lore links; bot command handles the zero-quest case.

Closing.

Done — merged in PR #193 (backend+bot `6d4a0d1`, frontend `ba5a714`). CI green. **Shipped:** - Extended `PlotThread` (no parallel model): new `QuestStatus` enum (`rumored/active/completed/failed/abandoned`), `player_visible`, `player_summary`; **dropped `resolved`**, data-migrated `resolved=true → completed` (migration `f2a3b4c5d6e7`, upgrade/downgrade/upgrade verified on a reset test DB). - GM thread endpoints carry the new fields; `notes` stays GM-only. New member-readable `GET /campaigns/{id}/quests` with a **separate `QuestResponse`** built by explicit field mapping — structurally can't leak `notes`. - Bot: `GET /api/bot/guilds/{guild_id}/quests` + `/quests` slash command (active + player-visible only). **Additive → no `BOT_CONTRACT_VERSION` bump.** - Frontend: `ThreadTracker` migrated `resolved`→`status` with a per-row visibility toggle + player-summary field; new read-only `QuestLog` on the campaign page (grouped Active/Rumored, resolved collapsed), never shows notes. **Tests:** backend 466 pass (+10 `test_quests.py`, incl. a raw-JSON-keys check that `notes` never reaches players/bot); frontend 206 (+4 `QuestLog`). **Acceptance criteria** all met: player token gets visible quests without `notes`; GM CRUD covers status/visibility/summary and toggling visibility immediately affects the player view; player view renders all five statuses with session/lore links; bot command handles the zero-quest case. Closing.
rbrooks referenced this issue from a commit 2026-07-18 04:59:00 +00:00
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Quest-Board#115
No description provided.