feat: one-click NPC/creature backstory + stat block flesh-out (#130) #197

Merged
claude-bot merged 2 commits from feat/130-npc-fleshout into main 2026-07-18 04:32:12 +00:00
Contributor

Closes #130. Adds two one-click GM actions on npc/creature wiki entries, reusing the existing LLM draft-review rail. Follows the GM-locked 2026-07-15 decisions.

Backend (c23fb42)

  • LoreEntryDraft gains an explicit mode enum (rephrase|merge|expand_backstory|generate_statblock, default rephrase — existing flows untouched) + a current_sidebar_fields JSONB staging column. Migration e8f9a0b1c2d3 (round-trips).
  • Two generators (audio_service): backstory is append-only (returns the full body with existing prose verbatim + a delineated ## Backstory section); stat block runs in JSON mode → {markdown, sidebar_fields} and is generated from the current backstory for coherence.
  • run_lore_entry_draft_generation branches on mode; stat-block stages markdown onto current_body and structured rows onto current_sidebar_fields, each defaulted to visibility="gm" (hidden from players).
  • POST drafts gains a mode discriminator, gated to npc/creature (400 otherwise; unknown mode 422). Approve appends staged rows onto the entry's sidebar_fields (never dropping existing) and snapshots a LoreEntryVersion.
  • PATCH drafts now accepts current_sidebar_fields — so the GM can edit values and flip each row's player-visibility in review before approving (re-normalised, visibility clamped/defaulted to gm). (This closed a gap in the first pass — without it the review UI couldn't persist the required per-row toggle.)

Frontend (<head>)

  • WikiArticle: "Generate backstory" / "Generate stat block" header actions, gated on GM + npc/creature + no existing draft; a mode-aware start modal (optional level/role/tone cue); navigates to review. One-draft-at-a-time + type errors surfaced inline.
  • WikiDraftReview: a prominent "Proposed stat block" section — per-row label/value + a visibility select defaulting to GM-only, with a "hidden from players by default" note. Edits persist via the existing debounced autosave PATCH and flush before approve/iterate. Non-stat-block drafts render exactly as before.

Tests

  • Backend: +14 (test_npc_fleshout.py) — the npc/creature gate, mode defaulting (rephrase/merge unchanged), worker staging (gm rows + append-only body), approve merge + version snapshot, failure paths, and the PATCH visibility-toggle. Full suite 516 pass; migration round-trip verified.
  • Frontend: +13 (campaigns api 4, WikiArticle 6, WikiDraftReview 3). 262 pass, eslint clean, vite build green.

Notes

  • Append-only + GM-only-by-default + review-gated: nothing lands without approval, generated stats are player-hidden until the GM explicitly opts a row in.
  • Regeneration/iterate bases generation on the entry's persisted body (not the draft's mutated body), so append is idempotent across iterations rather than compounding.
  • No bot/ changes → no BOT_CONTRACT_VERSION concern. Stat-schema-aware / game-aware generation remains out of scope (free-text game_system only), per the issue.

🤖 Generated with Claude Code

Closes #130. Adds two one-click GM actions on `npc`/`creature` wiki entries, reusing the existing LLM draft-review rail. Follows the GM-locked 2026-07-15 decisions. ## Backend (`c23fb42`) - `LoreEntryDraft` gains an explicit `mode` enum (`rephrase|merge|expand_backstory|generate_statblock`, default `rephrase` — existing flows untouched) + a `current_sidebar_fields` JSONB staging column. Migration `e8f9a0b1c2d3` (round-trips). - Two generators (`audio_service`): **backstory** is append-only (returns the full body with existing prose verbatim + a delineated `## Backstory` section); **stat block** runs in JSON mode → `{markdown, sidebar_fields}` and is generated **from the current backstory** for coherence. - `run_lore_entry_draft_generation` branches on `mode`; stat-block stages markdown onto `current_body` and structured rows onto `current_sidebar_fields`, each defaulted to **`visibility="gm"`** (hidden from players). - POST drafts gains a `mode` discriminator, **gated to npc/creature** (400 otherwise; unknown mode 422). **Approve** appends staged rows onto the entry's `sidebar_fields` (never dropping existing) and snapshots a `LoreEntryVersion`. - **PATCH drafts now accepts `current_sidebar_fields`** — so the GM can edit values and flip each row's player-visibility in review before approving (re-normalised, visibility clamped/defaulted to `gm`). *(This closed a gap in the first pass — without it the review UI couldn't persist the required per-row toggle.)* ## Frontend (`<head>`) - `WikiArticle`: "Generate backstory" / "Generate stat block" header actions, gated on GM + npc/creature + no existing draft; a mode-aware start modal (optional level/role/tone cue); navigates to review. One-draft-at-a-time + type errors surfaced inline. - `WikiDraftReview`: a prominent "Proposed stat block" section — per-row label/value + a **visibility select defaulting to GM-only**, with a "hidden from players by default" note. Edits persist via the existing debounced autosave PATCH and flush before approve/iterate. Non-stat-block drafts render exactly as before. ## Tests - Backend: +14 (`test_npc_fleshout.py`) — the npc/creature gate, mode defaulting (rephrase/merge unchanged), worker staging (gm rows + append-only body), approve merge + version snapshot, failure paths, and the **PATCH visibility-toggle**. Full suite **516 pass**; migration round-trip verified. - Frontend: +13 (campaigns api 4, WikiArticle 6, WikiDraftReview 3). **262 pass**, eslint clean, vite build green. ## Notes - Append-only + GM-only-by-default + review-gated: nothing lands without approval, generated stats are player-hidden until the GM explicitly opts a row in. - Regeneration/iterate bases generation on the entry's **persisted** body (not the draft's mutated body), so append is idempotent across iterations rather than compounding. - No `bot/` changes → no `BOT_CONTRACT_VERSION` concern. Stat-schema-aware / game-aware generation remains out of scope (free-text `game_system` only), per the issue. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Two GM actions on npc/creature wiki entries, reusing the existing LLM
draft-review rail (generating→ready→failed, review/iterate/approve→version
snapshot):

- LoreEntryDraft gains an explicit `mode` enum (rephrase|merge|
  expand_backstory|generate_statblock; default rephrase so existing flows are
  unchanged) and a `current_sidebar_fields` JSONB staging column; migration
  e8f9a0b1c2d3 (round-trips).
- audio_service: expand_lore_entry_backstory (append-only — returns full body
  with existing prose verbatim + a delineated ## Backstory section) and
  generate_lore_entry_statblock (json_mode → {markdown, sidebar_fields},
  generated FROM the current backstory for coherence).
- run_lore_entry_draft_generation branches on mode; statblock stages markdown
  onto current_body and structured rows onto current_sidebar_fields, each
  defaulted to visibility="gm" (hidden from players).
- POST drafts gains a `mode` discriminator, gated to npc/creature (400
  otherwise; unknown mode 422). approve appends staged rows onto the entry's
  sidebar_fields (never dropping existing) and snapshots a version.
- PATCH drafts now accepts current_sidebar_fields so the GM can edit values
  and flip each row's player-visibility in review before approving
  (re-normalised, visibility clamped/defaulted to gm).

Tests: +14 (test_npc_fleshout.py) — gate, mode defaulting, worker staging
(gm rows + append-only body), approve merge + version snapshot, failure
paths, and the PATCH visibility-toggle. Full suite 515 pass (pre-PATCH);
migration round-trip verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(frontend): one-click NPC/creature flesh-out + stat-block review (#130)
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 1m1s
CI / Docker image build (pull_request) Successful in 34s
CI / Frontend tests, audit, and build (pull_request) Successful in 1m58s
CI / Bot tests and audit (pull_request) Successful in 2m17s
CI / Backend migration, tests, and audit (pull_request) Successful in 4m44s
dd6ad34080
- api/campaigns.js: startLoreDraft sends `mode`; patchLoreDraft passes
  current_sidebar_fields through.
- WikiArticle: generalized RephraseModal → DraftStartModal (mode-aware);
  "Generate backstory" / "Generate stat block" header actions, gated on
  GM + entry_type npc|creature + no existing draft, each starting a draft
  and navigating to review (surfaces the 409 one-draft-at-a-time + type
  errors inline).
- WikiDraftReview: a "Proposed stat block" section for generate_statblock
  drafts — per-row label/value + a visibility select defaulting to GM-only,
  with a "hidden from players by default" note. Edits persist via the
  existing debounced patchLoreDraft autosave (current_sidebar_fields only
  sent when the draft has one) and are flushed before approve/iterate.
  Rephrase/backstory drafts render exactly as before.

Tests: +13 (campaigns api 4, WikiArticle 6, WikiDraftReview 3). 262 pass,
eslint clean, vite build succeeds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-bot deleted branch feat/130-npc-fleshout 2026-07-18 04:32:13 +00:00
Sign in to join this conversation.
No description provided.