feat: GM Workbench session prep sheet — first async/polling tool (#158) #221

Merged
claude-bot merged 1 commit from feat/158-session-prep into feat/v3.10-gm-workbench 2026-07-19 16:46:08 +00:00
Contributor

The highest-differentiation Workbench tool (per the planning report): it reads this campaign's state — the upcoming session's beat notes, recent session summaries, open plot threads, and the wiki — to draft a structured prep sheet no generic generator can replicate. Prose-heavy + multi-source, so it's the first tool to run async via the #136 Celery path, with the frontend polling for the result.

Backend

  • session_prep ToolDef (sync_allowed=False): SessionPrepParams(session_id) (str/UUID-validated so it round-trips through row.params to the Celery worker); context builders for beat_notes + recent_sessions_context + threads_context + lore_context; tolerant parser → {strong_start, scenes[], secrets_and_clues[], npcs[{name,note}], loose_ends[]}.
  • New GET /workbench/history/{result_id} — the single-result polling primitive for async tools (reuses the existing owned-result guard; GM-only).

Frontend

  • SessionPrepGenerator panel: pick an upcoming session → generate (returns pending) → poll fetchWorkbenchResult until ready/failed → render the sheet, linking any NPC whose name matches a wiki entry. Save via append-or-replace into beat_notes (existing PATCH) after explicit GM review — never auto-writes (meets the "never overwrite GM notes" criterion).
  • fetchWorkbenchResult uses cache: false — the shared client's 15s GET dedup cache would otherwise serve the stale pending body to every poll and the transition would never be observed.

Notes

  • No new models or migrations — the save path reuses the existing beat-notes PATCH; the sessions list already carries beat_notes.
  • The async generation core (run_workbench_generation_core) already existed (#136/#138); this is the first tool to exercise it end to end.
  • Backend by me; frontend polling panel by a delegate, then reviewed + verified here.

Verification (Docker)

  • Backend 719 passed (incl. test_workbench_session_prep.py — async core + beat-notes grounding, pending dispatch/enqueue, 422, GM-only, single-result GET 200/404, parser tolerance)
  • Frontend 379 passed / 39 files; prod build OK
  • ruff check + ruff format --check clean; eslint 0 errors

Closes #158.

The highest-differentiation Workbench tool (per the planning report): it reads **this** campaign's state — the upcoming session's beat notes, recent session summaries, open plot threads, and the wiki — to draft a structured prep sheet no generic generator can replicate. Prose-heavy + multi-source, so it's the first tool to run **async** via the #136 Celery path, with the frontend polling for the result. ## Backend - **`session_prep` ToolDef** (`sync_allowed=False`): `SessionPrepParams(session_id)` (str/UUID-validated so it round-trips through `row.params` to the Celery worker); context builders for `beat_notes` + `recent_sessions_context` + `threads_context` + `lore_context`; tolerant parser → `{strong_start, scenes[], secrets_and_clues[], npcs[{name,note}], loose_ends[]}`. - **New `GET /workbench/history/{result_id}`** — the single-result polling primitive for async tools (reuses the existing owned-result guard; GM-only). ## Frontend - **`SessionPrepGenerator`** panel: pick an upcoming session → generate (returns `pending`) → poll `fetchWorkbenchResult` until `ready`/`failed` → render the sheet, linking any NPC whose name matches a wiki entry. Save via **append-or-replace into `beat_notes`** (existing PATCH) after explicit GM review — **never auto-writes** (meets the "never overwrite GM notes" criterion). - `fetchWorkbenchResult` uses `cache: false` — the shared client's 15s GET dedup cache would otherwise serve the stale `pending` body to every poll and the transition would never be observed. ## Notes - No new models or migrations — the save path reuses the existing beat-notes PATCH; the sessions list already carries `beat_notes`. - The async generation core (`run_workbench_generation_core`) already existed (#136/#138); this is the first tool to exercise it end to end. - Backend by me; frontend polling panel by a delegate, then reviewed + verified here. ## Verification (Docker) - Backend **719 passed** (incl. `test_workbench_session_prep.py` — async core + beat-notes grounding, pending dispatch/enqueue, 422, GM-only, single-result GET 200/404, parser tolerance) - Frontend **379 passed** / 39 files; prod build OK - `ruff check` + `ruff format --check` clean; eslint 0 errors Closes #158.
The highest-differentiation Workbench tool: it reads *this* campaign's state
— the upcoming session's beat notes, recent session summaries, open plot
threads, and the wiki — to draft a structured prep sheet. Prose-heavy and
multi-source, so it runs async (sync_allowed=False) via the #136 Celery path.

Backend:
- session_prep ToolDef in GENERATOR_TOOLS: SessionPrepParams(session_id),
  context builders for beat_notes + recent_sessions + threads + lore, tolerant
  parser → {strong_start, scenes[], secrets_and_clues[], npcs[{name,note}],
  loose_ends[]}. session_id is a str param so it flows through row.params to
  the Celery worker.
- New GET /workbench/history/{result_id} — the single-result polling primitive
  for async tools.

Frontend:
- SessionPrepGenerator panel: pick an upcoming session, generate (pending),
  poll fetchWorkbenchResult until ready/failed, render the sheet with NPCs
  linked to matching wiki entries. Save via append-or-replace into beat_notes
  (existing PATCH) after explicit GM review — never auto-writes.
- fetchWorkbenchResult uses cache:false so polling isn't served the stale
  pending body by the client's 15s GET cache.

No new models or migrations. Backend 719 passed; frontend 379 passed;
ruff + eslint clean; build OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot merged commit 2eb4b6d045 into feat/v3.10-gm-workbench 2026-07-19 16:46:08 +00:00
claude-bot deleted branch feat/158-session-prep 2026-07-19 16:46:08 +00:00
Sign in to join this conversation.
No description provided.