[Game Systems] Carry the stats envelope through campaign export/import #214

Closed
opened 2026-07-19 00:21:45 +00:00 by claude-bot · 1 comment
Contributor

Motivation/Context

v3.9.0 follow-up (flagged in #139 / #145). #139 added the validated stats JSONB envelope ({system, schema_version, values, visibility}) on lore_entries, but the campaign export bundle builders (routers/campaigns.py, the lore-entry export dict ~:3651) and import_service construct lore dicts from an explicit key list that does not include stats. Result: exporting a campaign and re-importing it silently drops every entry's stat block — imported entries come back with stats = NULL. That's data loss on a round-trip, and undercuts the conversion wizard (#145) work a GM just did.

Approach

Export — add stats to the lore-entry export dict alongside sidebar_fields / timeline_events.

Import — restore stats in import_service when creating lore entries. The envelope self-describes its system + schema_version (it stores the system key, not a UUID), so store it verbatim — do NOT re-validate against the importing campaign's linked system. If the destination campaign links a different system (or none), the stored envelope simply renders read-only/"dormant" per #140's edge-state handling, and the GM can delete or re-link — exactly the designed unlink/relink behaviour. This keeps import non-destructive and preserves the GM's work.

Bundle versionstats is an additive optional key. Check how import_service validates the export schema version (there are existing test_import_rejects_invalid_schema_version tests): if importers ignore unknown keys and tolerate a missing stats, no version bump is needed; bump only if the version gate is strict. Older bundles (no stats key) must import fine with stats = None.

Out of scope

  • Re-validating or migrating imported stats to the destination campaign's schema (dormant-state handling already covers a mismatch).
  • The stats version-mismatch/"update to v{n}" flow (that's #140, already shipped).

Acceptance criteria

  • Exporting a campaign whose npc/creature entries have stats, then importing that bundle, yields entries whose stats envelope matches the original (system/schema_version/values/visibility preserved).
  • Importing an older bundle with no stats key succeeds, leaving stats = None.
  • Import stores the envelope verbatim (no re-validation); a destination-system mismatch is left to the existing dormant-state UI, not rejected on import.
  • Tests cover the round-trip (export → import preserves stats) and the old-bundle (no stats) path; existing import tests still pass.
## Motivation/Context v3.9.0 follow-up (flagged in #139 / #145). #139 added the validated `stats` JSONB envelope (`{system, schema_version, values, visibility}`) on `lore_entries`, but the campaign **export** bundle builders (`routers/campaigns.py`, the lore-entry export dict ~`:3651`) and `import_service` construct lore dicts from an explicit key list that does **not** include `stats`. Result: exporting a campaign and re-importing it **silently drops every entry's stat block** — imported entries come back with `stats = NULL`. That's data loss on a round-trip, and undercuts the conversion wizard (#145) work a GM just did. ## Approach **Export** — add `stats` to the lore-entry export dict alongside `sidebar_fields` / `timeline_events`. **Import** — restore `stats` in `import_service` when creating lore entries. The envelope **self-describes** its `system` + `schema_version` (it stores the system *key*, not a UUID), so store it **verbatim** — do NOT re-validate against the importing campaign's linked system. If the destination campaign links a different system (or none), the stored envelope simply renders read-only/"dormant" per #140's edge-state handling, and the GM can delete or re-link — exactly the designed unlink/relink behaviour. This keeps import non-destructive and preserves the GM's work. **Bundle version** — `stats` is an additive optional key. Check how `import_service` validates the export schema version (there are existing `test_import_rejects_invalid_schema_version` tests): if importers ignore unknown keys and tolerate a missing `stats`, no version bump is needed; bump only if the version gate is strict. Older bundles (no `stats` key) must import fine with `stats = None`. ## Out of scope - Re-validating or migrating imported stats to the destination campaign's schema (dormant-state handling already covers a mismatch). - The `stats` version-mismatch/"update to v{n}" flow (that's #140, already shipped). ## Acceptance criteria - Exporting a campaign whose npc/creature entries have `stats`, then importing that bundle, yields entries whose `stats` envelope matches the original (system/schema_version/values/visibility preserved). - Importing an **older** bundle with no `stats` key succeeds, leaving `stats = None`. - Import stores the envelope verbatim (no re-validation); a destination-system mismatch is left to the existing dormant-state UI, not rejected on import. - Tests cover the round-trip (export → import preserves stats) and the old-bundle (no stats) path; existing import tests still pass.
Author
Contributor

Picking this up as part of the v3.9.1 patch (with #213), on fix/v3.9.1-game-aware-followups → PR to main. Adds stats to the lore-entry export dict and restores it verbatim on import (self-describing envelope → dormant-state UI handles a system mismatch, no re-validation), with old-bundle (no stats) tolerance.

Picking this up as part of the **v3.9.1** patch (with #213), on `fix/v3.9.1-game-aware-followups` → PR to `main`. Adds `stats` to the lore-entry export dict and restores it verbatim on import (self-describing envelope → dormant-state UI handles a system mismatch, no re-validation), with old-bundle (no `stats`) tolerance.
Sign in to join this conversation.
No milestone
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#214
No description provided.