[Table Tools] One-click "Flesh out" NPC backstory and stat block in the wiki #130

Closed
opened 2026-07-15 03:56:02 +00:00 by claude-bot · 2 comments
Contributor

Motivation

A GM often creates a thin NPC lore entry (just a name, maybe a one-line note) and later wants it turned into a full character: a fleshed-out backstory plus a usable stat block. Today the only AI action on a wiki entry is "Rephrase with LLM", which rewrites existing prose without inventing new facts. There is no "expand this stub into a full NPC" action.

Goal: from an NPC or creature wiki article, generate (a) an expanded backstory and (b) a stat block — as two separate one-click actions that produce coherent results — presented as proposed drafts the GM reviews, edits, and approves. Never auto-written.

Decisions (locked by GM 2026-07-15)

  • Two separate actions — "Generate backstory" and "Generate stat block" — so the GM can regenerate either independently. They must be coherent together: the stat-block generator reads the current backstory (plus any level/role cues) so a thief's stats fit a thief of that level, etc. Implementation: stat-block generation always takes the current backstory as prompt context.
  • Append-only. Generation adds new sections; it never replaces existing GM-written prose. (Still routed through draft review before it lands.)
  • Applies to npc and creature entry types.
  • Stat block stored as BOTH free-form markdown in body and structured sidebar_fields (infobox rows) — readable and semi-machine-readable (eases a future Foundry push).
  • Player visibility: hidden by default. Generated stat blocks and structured fields are GM-only unless the GM explicitly marks them player-visible. Use the existing visibility key on sidebar_fields (public/gm/player_private); default all newly generated stat content to GM-only. Newly invented backstory content the GM hasn't vetted should likewise default to GM-only (or be clearly delineated). Confirm exact default granularity during implementation.

Approach — new draft modes on the existing rail

Reuse the existing LLM-draft pipeline (generating→ready→failed lifecycle, old-vs-new review UI, inline edit, iterate-with-feedback, versioning, approval). Add two modes alongside rephrase/merge: expand_backstory and generate_statblock.

Backend

  • Two new generation functions in audio_service.py modeled on rephrase_lore_entry_body (~line 717): an expand-backstory prompt and a stat-block prompt. The stat-block prompt consumes the entry's backstory + free-text game_system + any level/role signal and emits both a markdown block and a small set of structured {label, value, visibility} sidebar rows. Core via generate_structured_text (~line 1158; use json_mode for the structured half) and get_llm_config (Bot Settings, not env).
  • New draft modes in draft_service.py alongside start_rephrase_draft/start_merge_draft (~lines 63/93) and matching branches in run_lore_entry_draft_generation in reminder_tasks.py (~line 2079). Existing Celery draft task — no new task plumbing.
  • Reuse LoreEntryDraft and the existing draft endpoints in campaigns.py (POST .../lore/{entry_id}/drafts ~line 2212; review/iterate/approve ~2270-2389). LoreDraftCreate (~line 2148) gains a mode discriminator.
  • On approval: append markdown into body; write structured rows into sidebar_fields with GM-only visibility by default. Gate to entry_type in (npc, creature).

Frontend

  • Two GM-only buttons — "Generate backstory", "Generate stat block" — next to "Rephrase with LLM" in WikiArticle.jsx header actions (~line 1660), shown for npc/creature entries. Reuse the modal→draft→review loop into WikiDraftReview.jsx. Client calls in campaigns.js alongside startLoreDraft. Review UI must make the per-row player-visibility toggle obvious (default off).

Coherence requirement

Stats must reflect the backstory. Simplest guarantee: stat-block generation always consumes the current backstory as context, and the GM regenerates stats after editing the backstory. Consider a hint in the review UI when the backstory changed after the stat block was generated (stale-stats warning).

  • Game-aware systems (structured stat schemas instead of free-form game_system) — a much larger effort under separate investigation. This issue stays system-agnostic (LLM free-form guided by the game_system hint). If game-aware work lands, the stat-block generator can target a real schema. Storing stats as structured sidebar_fields is chosen partly to smooth that transition.
  • FoundryVTT push of generated stats/tokens — tracked under the Foundry pillar (#10, v4.1.0) and a separate live-push investigation. Not built here.
  • Game-system-aware / validated stat generation (no rules engine exists).
  • Bulk "flesh out every stub" — single-entry, on-demand only.

Acceptance

  • On an npc/creature article, "Generate backstory" and "Generate stat block" each produce a proposed draft; stats are coherent with the backstory.
  • Generated content is append-only and GM-only by default; player visibility requires explicit GM opt-in.
  • Everything flows through the existing draft review UI (edit, iterate, approve/discard); nothing lands without approval; approval snapshots a LoreEntryVersion.
  • Stat block is stored as both markdown in body and structured sidebar_fields.
  • Generation uses the LLM from Admin → Bot Settings via the existing Celery draft task.
## Motivation A GM often creates a thin NPC lore entry (just a name, maybe a one-line note) and later wants it turned into a full character: a fleshed-out backstory plus a usable stat block. Today the only AI action on a wiki entry is **"Rephrase with LLM"**, which rewrites existing prose without inventing new facts. There is no "expand this stub into a full NPC" action. **Goal:** from an NPC or creature wiki article, generate (a) an expanded backstory and (b) a stat block — as **two separate one-click actions** that produce coherent results — presented as proposed drafts the GM reviews, edits, and approves. Never auto-written. ## Decisions (locked by GM 2026-07-15) - **Two separate actions** — "Generate backstory" and "Generate stat block" — so the GM can regenerate either independently. **They must be coherent together:** the stat-block generator reads the current backstory (plus any level/role cues) so a thief's stats fit a thief of that level, etc. Implementation: stat-block generation always takes the current backstory as prompt context. - **Append-only.** Generation adds new sections; it never replaces existing GM-written prose. (Still routed through draft review before it lands.) - **Applies to `npc` and `creature` entry types.** - **Stat block stored as BOTH** free-form markdown in `body` **and** structured `sidebar_fields` (infobox rows) — readable *and* semi-machine-readable (eases a future Foundry push). - **Player visibility: hidden by default.** Generated stat blocks and structured fields are **GM-only unless the GM explicitly marks them player-visible.** Use the existing `visibility` key on `sidebar_fields` (public/gm/player_private); default all newly generated stat content to GM-only. Newly invented backstory content the GM hasn't vetted should likewise default to GM-only (or be clearly delineated). Confirm exact default granularity during implementation. ## Approach — new draft *modes* on the existing rail Reuse the existing LLM-draft pipeline (generating→ready→failed lifecycle, old-vs-new review UI, inline edit, iterate-with-feedback, versioning, approval). Add two modes alongside `rephrase`/`merge`: `expand_backstory` and `generate_statblock`. **Backend** - Two new generation functions in [audio_service.py](webapp/backend/app/services/audio_service.py) modeled on `rephrase_lore_entry_body` (~line 717): an **expand-backstory** prompt and a **stat-block** prompt. The stat-block prompt consumes the entry's backstory + free-text `game_system` + any level/role signal and emits both a markdown block and a small set of structured `{label, value, visibility}` sidebar rows. Core via `generate_structured_text` (~line 1158; use `json_mode` for the structured half) and `get_llm_config` (Bot Settings, not env). - New draft modes in [draft_service.py](webapp/backend/app/services/draft_service.py) alongside `start_rephrase_draft`/`start_merge_draft` (~lines 63/93) and matching branches in `run_lore_entry_draft_generation` in [reminder_tasks.py](webapp/backend/app/tasks/reminder_tasks.py) (~line 2079). Existing Celery draft task — no new task plumbing. - Reuse `LoreEntryDraft` and the existing draft endpoints in [campaigns.py](webapp/backend/app/routers/campaigns.py) (`POST .../lore/{entry_id}/drafts` ~line 2212; review/iterate/approve ~2270-2389). `LoreDraftCreate` (~line 2148) gains a `mode` discriminator. - On approval: **append** markdown into `body`; write structured rows into `sidebar_fields` with **GM-only `visibility` by default**. Gate to `entry_type in (npc, creature)`. **Frontend** - Two GM-only buttons — "Generate backstory", "Generate stat block" — next to "Rephrase with LLM" in [WikiArticle.jsx](webapp/frontend/src/pages/WikiArticle.jsx) header actions (~line 1660), shown for npc/creature entries. Reuse the modal→draft→review loop into [WikiDraftReview.jsx](webapp/frontend/src/pages/WikiDraftReview.jsx). Client calls in [campaigns.js](webapp/frontend/src/api/campaigns.js) alongside `startLoreDraft`. Review UI must make the per-row **player-visibility toggle obvious (default off)**. ## Coherence requirement Stats must reflect the backstory. Simplest guarantee: stat-block generation always consumes the current backstory as context, and the GM regenerates stats after editing the backstory. Consider a hint in the review UI when the backstory changed after the stat block was generated (stale-stats warning). ## Related / future (out of scope here) - **Game-aware systems** (structured stat schemas instead of free-form `game_system`) — a much larger effort under separate investigation. This issue stays system-agnostic (LLM free-form guided by the `game_system` hint). If game-aware work lands, the stat-block generator can target a real schema. Storing stats as structured `sidebar_fields` is chosen partly to smooth that transition. - **FoundryVTT push** of generated stats/tokens — tracked under the Foundry pillar (#10, v4.1.0) and a separate live-push investigation. Not built here. - Game-system-aware / validated stat generation (no rules engine exists). - Bulk "flesh out every stub" — single-entry, on-demand only. ## Acceptance - On an npc/creature article, "Generate backstory" and "Generate stat block" each produce a proposed draft; stats are coherent with the backstory. - Generated content is **append-only** and **GM-only by default**; player visibility requires explicit GM opt-in. - Everything flows through the existing draft review UI (edit, iterate, approve/discard); nothing lands without approval; approval snapshots a `LoreEntryVersion`. - Stat block is stored as **both** markdown in `body` and structured `sidebar_fields`. - Generation uses the LLM from Admin → Bot Settings via the existing Celery draft task.
Author
Contributor

GM decisions captured and folded into the body:

  1. Two separate actions (backstory / stat block), individually regenerable, but coherent — stat-block generation always reads the current backstory + level/role so the numbers fit the character.
  2. Append-only (never overwrites GM prose; still review-gated).
  3. Applies to npc and creature entries.
  4. Stat block stored as both markdown (body) and structured sidebar_fields.
  5. Player visibility off by default — generated stats/fields are GM-only unless the GM explicitly opts a row into player-visible, via the existing sidebar_fields.visibility model.

Game-aware stat schemas and FoundryVTT live-push are being investigated separately as larger efforts; this issue stays system-agnostic and wiki-only.

GM decisions captured and folded into the body: 1. **Two separate actions** (backstory / stat block), individually regenerable, but coherent — stat-block generation always reads the current backstory + level/role so the numbers fit the character. 2. **Append-only** (never overwrites GM prose; still review-gated). 3. Applies to **`npc` and `creature`** entries. 4. Stat block stored as **both** markdown (`body`) **and** structured `sidebar_fields`. 5. **Player visibility off by default** — generated stats/fields are GM-only unless the GM explicitly opts a row into player-visible, via the existing `sidebar_fields.visibility` model. Game-aware stat schemas and FoundryVTT live-push are being investigated separately as larger efforts; this issue stays system-agnostic and wiki-only.
Author
Contributor

Done — merged in PR #197 (backend c23fb42 + frontend). CI green.

Shipped (all GM-locked decisions honored):

  • LoreEntryDraft gains explicit mode (rephrase|merge|expand_backstory|generate_statblock, default rephrase) + current_sidebar_fields staging column; migration e8f9a0b1c2d3 (round-trips).
  • Two actions on npc/creature entries, reusing the existing draft-review rail: Generate backstory (append-only — existing prose preserved verbatim + a delineated ## Backstory section) and Generate stat block (json_mode → markdown and structured sidebar_fields; generated from the current backstory for coherence).
  • Player visibility off by default — generated stat rows default to visibility="gm"; the review UI surfaces a prominent per-row visibility toggle (Public / GM-only / Player-private) with a "hidden from players by default" note, persisted via PATCH before approve.
  • Append-only + review-gated: approve appends staged rows onto the entry's sidebar_fields (never dropping existing) and snapshots a LoreEntryVersion; nothing lands without GM approval.
  • Gated to npc/creature (400 otherwise); rephrase/merge flows unchanged.

Tests: backend 516 pass (+14 test_npc_fleshout.py, incl. the PATCH visibility-toggle); frontend 262 (+13).

Acceptance criteria met: both actions produce review drafts; stats coherent with backstory; content append-only + GM-only by default with explicit opt-in; everything flows through edit/iterate/approve/discard with a version snapshot; stat block stored as both markdown (body) and structured sidebar_fields; generation uses the Admin → Bot Settings LLM via the existing Celery draft task.

Out of scope as noted: game-aware/validated stat schemas and FoundryVTT push remain under their separate tracks.

Closing.

Done — merged in PR #197 (backend `c23fb42` + frontend). CI green. **Shipped (all GM-locked decisions honored):** - `LoreEntryDraft` gains explicit `mode` (`rephrase|merge|expand_backstory|generate_statblock`, default rephrase) + `current_sidebar_fields` staging column; migration `e8f9a0b1c2d3` (round-trips). - **Two actions** on npc/creature entries, reusing the existing draft-review rail: **Generate backstory** (append-only — existing prose preserved verbatim + a delineated `## Backstory` section) and **Generate stat block** (json_mode → markdown **and** structured `sidebar_fields`; generated **from the current backstory** for coherence). - **Player visibility off by default** — generated stat rows default to `visibility="gm"`; the review UI surfaces a prominent per-row visibility toggle (Public / GM-only / Player-private) with a "hidden from players by default" note, persisted via PATCH before approve. - **Append-only + review-gated**: approve appends staged rows onto the entry's `sidebar_fields` (never dropping existing) and snapshots a `LoreEntryVersion`; nothing lands without GM approval. - Gated to npc/creature (400 otherwise); rephrase/merge flows unchanged. **Tests:** backend 516 pass (+14 `test_npc_fleshout.py`, incl. the PATCH visibility-toggle); frontend 262 (+13). **Acceptance criteria** met: both actions produce review drafts; stats coherent with backstory; content append-only + GM-only by default with explicit opt-in; everything flows through edit/iterate/approve/discard with a version snapshot; stat block stored as both markdown (body) and structured `sidebar_fields`; generation uses the Admin → Bot Settings LLM via the existing Celery draft task. Out of scope as noted: game-aware/validated stat schemas and FoundryVTT push remain under their separate tracks. 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#130
No description provided.