[Workbench] Session Prep sheet regurgitates the last session — no working channel for GM intent about the upcoming one #305

Closed
opened 2026-08-12 00:45:30 +00:00 by claude-bot · 3 comments
Contributor

Symptom

Session Prep suggestions are mostly restatements of what happened last session rather than proposals for the next one. Reported during real session prep: "pretty useless."

Cause

The prep prompt assembles four context blocks (generation_service.py:1636-1650):

Block Source
_session_prep_notes_context the selected session's beat_notes
_session_prep_recent_context recent_sessions_context(n=2) — last 2 summarised sessions
_session_prep_threads_context open plot threads
_session_prep_lore_context campaign wiki

Three of the four are retrospective or static. The only forward-looking input is beat_notes — and that block drops out entirely when notes are empty (generation_service.py:1094-1105):

if session is None or not session.beat_notes:
    return ""

Two active bugs mean it is almost always empty in practice:

  • Beat notes have no read path, so the Beat Planner textarea always opens blank and "Save Notes" can null them out — see the beat-notes round-trip issue. Notes the GM did write are likely already gone.
  • The session picker defaults to the furthest-out occurrence of the series, so prep often targets a session that was never going to have notes — see the session-picker issue.

With the one prospective block missing, the model is left with two session summaries — the newest of which gets the largest character budget (generation_service.py:251-259, _RECENT_SUMMARY_CHARS_LATEST vs _RECENT_SUMMARY_CHARS_OLDER) — plus threads and lore. Recapping last session is the most probable output given that input. The system prompt's "do not invent major new facts" instruction (generation_service.py:1035-1054) pushes further in the same direction.

To answer the question directly: yes, GM comments are already pulled in

beat_notes is the "notes for the AI" channel — it's fed straight into the prompt as "GM's existing beat notes for {title}". There is no second free-text field. It just hasn't been reaching the model.

Proposed fix

  1. Unblock the existing channel first (dependencies — likely most of the fix on its own): fix the beat-notes round-trip and the session-picker default.
  2. Make the beat-notes input visible where prep happens. Right now the GM has to know to go to the separate Beat Planner tool and write notes against the correct session before generating. Surface an editable "notes for this session" box directly in the Session Prep panel, bound to the selected session's beat_notes, so the steer is where the generate button is.
  3. Optionally add a one-shot steer field — a non-persisted "what I want from this session" textarea folded into the prompt as an extra context block. Distinct from beat_notes (which is durable prep the GM keeps); this is per-run direction like "they're heading to the capital, focus on the guild politics." Cheap: a new field on SessionPrepParams plus one context builder.
  4. Rebalance the prompt. Recent summaries should be framed as what to build on and not repeat, not as material to restate. Consider instructing the model explicitly that scenes and secrets_and_clues must advance beyond what the summaries describe, and that loose_ends is the only section meant to look backwards. Worth an eval pass against a real campaign after (1)–(3) land, since some of the current behaviour may simply be the empty-context failure mode.

Depends on the beat-notes round-trip and session-picker default issues filed alongside this.

## Symptom Session Prep suggestions are mostly restatements of what happened *last* session rather than proposals for the next one. Reported during real session prep: "pretty useless." ## Cause The prep prompt assembles four context blocks ([generation_service.py:1636-1650](webapp/backend/app/services/generation_service.py#L1636-L1650)): | Block | Source | |---|---| | `_session_prep_notes_context` | the selected session's `beat_notes` | | `_session_prep_recent_context` | `recent_sessions_context(n=2)` — last 2 summarised sessions | | `_session_prep_threads_context` | open plot threads | | `_session_prep_lore_context` | campaign wiki | Three of the four are **retrospective or static**. The only forward-looking input is `beat_notes` — and that block drops out entirely when notes are empty ([generation_service.py:1094-1105](webapp/backend/app/services/generation_service.py#L1094-L1105)): ```python if session is None or not session.beat_notes: return "" ``` Two active bugs mean it is almost always empty in practice: - Beat notes have no read path, so the Beat Planner textarea always opens blank and "Save Notes" can null them out — see the beat-notes round-trip issue. Notes the GM did write are likely already gone. - The session picker defaults to the furthest-out occurrence of the series, so prep often targets a session that was never going to have notes — see the session-picker issue. With the one prospective block missing, the model is left with two session summaries — the newest of which gets the largest character budget ([generation_service.py:251-259](webapp/backend/app/services/generation_service.py#L251-L259), `_RECENT_SUMMARY_CHARS_LATEST` vs `_RECENT_SUMMARY_CHARS_OLDER`) — plus threads and lore. Recapping last session is the most probable output given that input. The system prompt's "do not invent major new facts" instruction ([generation_service.py:1035-1054](webapp/backend/app/services/generation_service.py#L1035-L1054)) pushes further in the same direction. ## To answer the question directly: yes, GM comments are already pulled in `beat_notes` **is** the "notes for the AI" channel — it's fed straight into the prompt as *"GM's existing beat notes for {title}"*. There is no second free-text field. It just hasn't been reaching the model. ## Proposed fix 1. **Unblock the existing channel first** (dependencies — likely most of the fix on its own): fix the beat-notes round-trip and the session-picker default. 2. **Make the beat-notes input visible where prep happens.** Right now the GM has to know to go to the separate Beat Planner tool and write notes against the correct session before generating. Surface an editable "notes for this session" box directly in the Session Prep panel, bound to the selected session's `beat_notes`, so the steer is where the generate button is. 3. **Optionally add a one-shot steer field** — a non-persisted "what I want from this session" textarea folded into the prompt as an extra context block. Distinct from `beat_notes` (which is durable prep the GM keeps); this is per-run direction like "they're heading to the capital, focus on the guild politics." Cheap: a new field on `SessionPrepParams` plus one context builder. 4. **Rebalance the prompt.** Recent summaries should be framed as *what to build on and not repeat*, not as material to restate. Consider instructing the model explicitly that `scenes` and `secrets_and_clues` must advance beyond what the summaries describe, and that `loose_ends` is the only section meant to look backwards. Worth an eval pass against a real campaign after (1)–(3) land, since some of the current behaviour may simply be the empty-context failure mode. ## Related Depends on the beat-notes round-trip and session-picker default issues filed alongside this.
Author
Contributor

Cluster cross-reference — all four came out of the same session-prep run on 2026-08-11:

  • #302 — session pickers default to the furthest-out session
  • #303 — beat notes round-trip / append-is-really-replace / Beat Planner wipe
  • #304 — generated output lost on tool switch; "Re-open" doesn't restore it
  • #305 (this) — prep sheet regurgitates last session; no working channel for GM intent

Depends on #303 and #302. Re-evaluate the output quality after those land before investing in the prompt rebalance (step 4) — a good share of the current behaviour is probably just the empty-beat-notes failure mode.

Cluster cross-reference — all four came out of the same session-prep run on 2026-08-11: - **#302** — session pickers default to the furthest-out session - **#303** — beat notes round-trip / append-is-really-replace / Beat Planner wipe - **#304** — generated output lost on tool switch; "Re-open" doesn't restore it - **#305** (this) — prep sheet regurgitates last session; no working channel for GM intent Depends on **#303** and **#302**. Re-evaluate the output quality after those land before investing in the prompt rebalance (step 4) — a good share of the current behaviour is probably just the empty-beat-notes failure mode.
Author
Contributor

Picking this up as v4.3.0 phase 3 (#514), after #302 and #303 unblock the notes channel. Then: an editable "Notes for this session" box in the Session Prep panel bound to the session's notes, a one-shot "What I want from this session" steer folded into the prompt as its own block, and the prompt rebalanced so recent summaries are what to build on rather than restate, with the loose-ends section the only one allowed to look back. The backend test asserts on the assembled prompt, not on a mocked generator.

Picking this up as v4.3.0 phase 3 (#514), after #302 and #303 unblock the notes channel. Then: an editable "Notes for this session" box in the Session Prep panel bound to the session's notes, a one-shot "What I want from this session" steer folded into the prompt as its own block, and the prompt rebalanced so recent summaries are what to build on rather than restate, with the loose-ends section the only one allowed to look back. The backend test asserts on the assembled prompt, not on a mocked generator.
Author
Contributor

Done in the phase 3 PR (auto-merging on green); ships with v4.3.0. Built after #302 and #303, which was the right order: the reason the prompt regurgitated was that its only forward-looking block was almost always empty.

Three changes:

  1. The notes editor is now in the Session Prep panel, bound to the selected session through #303's GET/PATCH, as a field control that holds no value and never reaches buildParams. Notes get written where asking for the sheet happens, not behind a different palette entry.
  2. direction on SessionPrepParams: a one-shot steer, folded in by its own context builder registered last, so it lands next to the closing instruction instead of behind the wiki dump. Whitespace normalises to None, so a blank box adds no empty block.
  3. The prompt now says what the recent summaries are for: scenes and secrets_and_clues must advance past them, and loose_ends is named as the only backward-looking section.

Per this project's testing feedback, a mocked generate_structured_text cannot tell a good prompt from a bad one, so the tests assert on the assembled prompt and the system_prompt kwarg. The framing sentence is exported as SESSION_PREP_FRAMING and asserted by identity rather than a copied string, and the steer is checked both for presence and for being the last block. An eval pass against a real campaign, as the issue suggests, is worth doing after the release once the empty-context failure mode is out of the way.

Done in the phase 3 PR (auto-merging on green); ships with v4.3.0. Built after #302 and #303, which was the right order: the reason the prompt regurgitated was that its only forward-looking block was almost always empty. Three changes: 1. The notes editor is now **in** the Session Prep panel, bound to the selected session through #303's GET/PATCH, as a field control that holds no value and never reaches `buildParams`. Notes get written where asking for the sheet happens, not behind a different palette entry. 2. `direction` on `SessionPrepParams`: a one-shot steer, folded in by its own context builder registered **last**, so it lands next to the closing instruction instead of behind the wiki dump. Whitespace normalises to `None`, so a blank box adds no empty block. 3. The prompt now says what the recent summaries are *for*: `scenes` and `secrets_and_clues` must advance past them, and `loose_ends` is named as the only backward-looking section. Per this project's testing feedback, a mocked `generate_structured_text` cannot tell a good prompt from a bad one, so the tests assert on the assembled prompt and the `system_prompt` kwarg. The framing sentence is exported as `SESSION_PREP_FRAMING` and asserted by identity rather than a copied string, and the steer is checked both for presence and for being the last block. An eval pass against a real campaign, as the issue suggests, is worth doing after the release once the empty-context failure mode is out of the way.
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#305
No description provided.