feat: campaign templates and cloning (#125) #1215

Merged
claude-bot merged 59 commits from feat/125-campaign-templates into main 2026-10-17 17:01:25 +00:00
Collaborator

Fixes #125: campaign templates and cloning. Design: docs/design/campaign-templates.md (branch design/125-campaign-templates). The owner answered Q188 to Q191, all (a).

Server

  • Migration a125c0b1e5e1 (revises a126ca1e0d01, the calendar). It adds:
    • campaigns.one_shot_archive_dismissed_at;
    • campaign_invites.invited_role (player or gm);
    • import_jobs.kind and clone_args;
    • a wider notification CHECK for campaign_invitation.
  • A setup flavour of the export. It uses the same schema (21) with setup_only, keeps an allow-list of sections, and never carries history:
    • links to pages left behind become plain words;
    • session references are dropped, while world dates are kept;
    • quests come back not started;
    • personal notes are never copied, and GM-only stays GM-only.
  • GET …/setup lists the pages by kind with default ticks: the world is ticked, and player characters, quests and pages that came from a session are not.
  • POST …/setup-file returns the setup zip.
  • POST …/clone runs a background job through the import machinery. The new campaign is owned by the caller, and both campaigns get an audit row.
    • Campaign content packs come along only with a fresh affirmation.
    • Lines & Veils follow "Invite the same people".
  • Invite the same people makes one invite per member (GMs come back as GMs) and sends it as a campaign_invitation notification, in the bell and by email under each person's own settings. It carries only a line and the link.
  • The one-shot: reminders at 24 hours only, no series, the nudge off, and a single "archive it?" in Needs you after it's played.
  • The import sweep re-queues a clone as a clone. Jobs say their kind.

Screens

  • One dialog for "Start a new campaign from this one" and "Save the setup as a file", with clone progress.
  • "Start from a file" on New campaign.
  • The Needs you row.
  • "Invited as GM" on the invites list.
  • The bell's invitation row.

Tests

  • Backend: test_campaign_setup_copy (48+), export, notifications and parity, 256 in the latest run; the full suite in the 4.6.0 rehearsal passed 10,333.
  • Vitest: targeted, with load timeouts rerun alone.
  • Journeys: 187 in the rehearsal, which ran before the final templates additions, so this PR's CI covers those.

Squash on merge.

🤖 Generated with Claude Code

Fixes #125: campaign templates and cloning. Design: `docs/design/campaign-templates.md` (branch `design/125-campaign-templates`). The owner answered Q188 to Q191, all (a). **Server** - **Migration `a125c0b1e5e1`** (revises `a126ca1e0d01`, the calendar). It adds: - `campaigns.one_shot_archive_dismissed_at`; - `campaign_invites.invited_role` (player or gm); - `import_jobs.kind` and `clone_args`; - a wider notification CHECK for `campaign_invitation`. - **A setup flavour of the export.** It uses the same schema (21) with `setup_only`, keeps an allow-list of sections, and never carries history: - links to pages left behind become plain words; - session references are dropped, while world dates are kept; - quests come back not started; - personal notes are never copied, and GM-only stays GM-only. - **`GET …/setup`** lists the pages by kind with default ticks: the world is ticked, and player characters, quests and pages that came from a session are not. - **`POST …/setup-file`** returns the setup zip. - **`POST …/clone`** runs a background job through the import machinery. The new campaign is owned by the caller, and both campaigns get an audit row. - Campaign content packs come along only with a fresh affirmation. - Lines & Veils follow "Invite the same people". - **Invite the same people** makes one invite per member (GMs come back as GMs) and **sends** it as a `campaign_invitation` notification, in the bell and by email under each person's own settings. It carries only a line and the link. - **The one-shot:** reminders at 24 hours only, no series, the nudge off, and a single "archive it?" in Needs you after it's played. - **The import sweep** re-queues a clone as a clone. Jobs say their `kind`. **Screens** - One dialog for "Start a new campaign from this one" and "Save the setup as a file", with clone progress. - "Start from a file" on New campaign. - The Needs you row. - "Invited as GM" on the invites list. - The bell's invitation row. **Tests** - Backend: `test_campaign_setup_copy` (48+), export, notifications and parity, 256 in the latest run; the full suite in the 4.6.0 rehearsal passed 10,333. - Vitest: targeted, with load timeouts rerun alone. - Journeys: 187 in the rehearsal, which ran before the final templates additions, so this PR's CI covers those. Squash on merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Entries carry an id; check_change derives the remap from ids and remap_dates
rewrites stored indexes. Festival and leap days can sit outside the week.
scripts/calendar_cases.py builds cases.json from brute-force enumeration.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Merge main into feat/126-calendar; its migration follows a1181f11e5a1; Files and Calendar both in Settings
All checks were successful
CI / Does this change touch the recording path? (pull_request) Successful in 21s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Which journeys does this change need? (pull_request) Successful in 28s
CI / Bot/backend version sync (pull_request) Successful in 49s
CI / Docker image build (pull_request) Successful in 57s
CI / Backend lint (ruff) (pull_request) Successful in 58s
CI / Feedback site tests (pull_request) Successful in 1m29s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m30s
CI / Bot tests and audit (pull_request) Successful in 2m36s
CI / Frontend (shard 2/2) (pull_request) Successful in 8m42s
CI / Frontend (shard 1/2) (pull_request) Successful in 9m57s
CI / End-to-end journeys (phone) (pull_request) Successful in 21m12s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 22m51s
CI / End-to-end journeys (desktop) (pull_request) Successful in 22m54s
CI / Backend migration, tests, and audit (pull_request) Successful in 34m1s
d47c929de3
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merge main into feat/125-campaign-templates
Some checks failed
CI / Backend lint (ruff) (pull_request) Successful in 27s
CI / Which journeys does this change need? (pull_request) Successful in 12s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 59s
CI / Does this change touch the recording path? (pull_request) Successful in 29s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Bot/backend version sync (pull_request) Successful in 57s
CI / Docker image build (pull_request) Successful in 1m8s
CI / Bot tests and audit (pull_request) Successful in 2m6s
CI / Feedback site tests (pull_request) Successful in 1m43s
CI / Frontend (shard 2/2) (pull_request) Successful in 10m35s
CI / Frontend (shard 1/2) (pull_request) Successful in 13m16s
CI / End-to-end journeys (desktop) (pull_request) Failing after 28m7s
CI / Backend migration, tests, and audit (pull_request) Has been cancelled
bba6d8fef8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude-bot scheduled this pull request to auto merge when all checks succeed 2026-10-17 15:39:31 +00:00
test(e2e): J1 counts five ways to start a campaign (#125)
All checks were successful
CI / Backend lint (ruff) (pull_request) Successful in 33s
CI / Bot/backend version sync (pull_request) Successful in 30s
CI / Does this change touch the recording path? (pull_request) Successful in 15s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Which journeys does this change need? (pull_request) Successful in 13s
CI / Feedback site tests (pull_request) Successful in 56s
CI / Bot tests and audit (pull_request) Successful in 2m11s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 2m40s
CI / Docker image build (pull_request) Successful in 3m2s
CI / Frontend (shard 1/2) (pull_request) Successful in 12m34s
CI / Frontend (shard 2/2) (pull_request) Successful in 21m44s
CI / End-to-end journeys (phone) (pull_request) Successful in 18m19s
CI / End-to-end journeys (desktop) (pull_request) Successful in 19m22s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 25m30s
CI / Backend migration, tests, and audit (pull_request) Successful in 39m47s
0de6bcb8a8
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign in to join this conversation.
No description provided.