Demo seed: The Amberwake Accord (GM copy), with the owner in the GM's seat (#951) #954

Merged
claude-bot merged 4 commits from build/demo-gm-copy into main 2026-09-30 06:38:48 +00:00
Collaborator

Closes #951. The owner's review of lanes 0–10 has about 40 GM-only pieces, and on dev the owner is only ever a player in the demo campaigns. This adds a fifth demo campaign, "The Amberwake Accord (GM copy)": Amberwake mirrored row for row under its own ids, with two seats swapped. The owner sits in Dana's GM seat (and owns the campaign); Dana sits in the owner's player seat and plays the owner's character, Hollis.

How it works:

  • The variant: demo/engine.py has a frozen Variant(salt, name, swap_seats).
    • AMBERWAKE is the original; GM_COPY has salt gm-copy.
    • Variant.did() salts every deterministic id except people (user:*, which are shared).
  • The builder: it takes the variant.
    • self.main_id and self.did() replace the module constants (a mechanical rewrite across Builder and WikiMixin).
    • self.dana became self.gm, and uid() maps the member and Dana through the seats.
    • The engine's own GM strings ("Edited by …", "Rewritten by …", the pack publisher, audit lines) name the owner in the copy.
  • The build: it runs the original, then mirror():
    • the main campaign, prep, lane 8 and the party;
    • not the people, the Tuesday Tide or the admin campaigns.
  • --reset and the guards: GM_COPY_ID is in CAMPAIGN_IDS, so --reset, delete_demo, the "elsewhere" guard and write_media's folder guard cover it with no other change. Everything stays inside the same armed tripwires and one transaction.
  • Notifications: the copy's bell is a GM's:
    • transcript ready and failed, wiki suggestions, a member joined, ownership answered, safety waiting, and threads started;
    • plus session_at_risk and audio_deletion_pending, which only a GM gets.
    • Dana's bell is the owner's Amberwake player bell, re-addressed.

Deliberately identical: transcripts and world text. So "Dana" still appears inside content (the table talk, and a few template lines). Only the engine's own attributions change.

Not mirrored:

  • the Tuesday Tide's share of lanes 6 and 8;
  • the other three campaigns;
  • content notices;
  • the two admin audit events about Amberwake's pack.

Checks:

  • test_demo_seed.py: 6 pass. The new mirror test covers:
    • the copy's name, owner and seats;
    • equal per-table counts across 59 tables (notifications +2, audit −2, as above);
    • GM-authored rows attributed to Dana in Amberwake and to the owner in the copy (a prep sheet, an approval, a reveal, a bot test);
    • :::for rendering for Dana and the GM, and not for Marcus;
    • the GM bell's kinds;
    • the round trip for --reset, and files in each campaign's own folder (76 each).
  • The CLI on a freshly migrated database:
    • the first run wrote 5 campaigns and 152 files;
    • a re-run said "already exists" and exited 0;
    • --reset rebuilt it;
    • --dry-run wrote nothing.
  • The originals are unchanged: main's seed and this branch's were compared row by row in fresh databases. Every main row is present, except for server now() timestamps and one existing random default (a quest page's lore_entries.version_id). The branch's extra rows are exactly the copy.
  • Lint and changelog: ruff check and format are clean, and changelog.py check passes.

On dev: the next deploy re-runs the seed with --reset. The README says how.

Changelog: changelog.d/951-demo-gm-copy.added.md.

🤖 Generated with Claude Code

Closes #951. The owner's review of lanes 0–10 has about 40 GM-only pieces, and on dev the owner is only ever a player in the demo campaigns. This adds a **fifth demo campaign, "The Amberwake Accord (GM copy)":** Amberwake mirrored row for row under its own ids, with two seats swapped. The owner sits in Dana's GM seat (and owns the campaign); Dana sits in the owner's player seat and plays the owner's character, Hollis. **How it works:** - **The variant:** `demo/engine.py` has a frozen `Variant(salt, name, swap_seats)`. - `AMBERWAKE` is the original; `GM_COPY` has salt `gm-copy`. - `Variant.did()` salts every deterministic id except people (`user:*`, which are shared). - **The builder:** it takes the variant. - `self.main_id` and `self.did()` replace the module constants (a mechanical rewrite across `Builder` and `WikiMixin`). - `self.dana` became `self.gm`, and `uid()` maps the member and Dana through the seats. - The engine's own GM strings ("Edited by …", "Rewritten by …", the pack publisher, audit lines) name the owner in the copy. - **The build:** it runs the original, then `mirror()`: - the main campaign, prep, lane 8 and the party; - not the people, the Tuesday Tide or the admin campaigns. - **`--reset` and the guards:** `GM_COPY_ID` is in `CAMPAIGN_IDS`, so `--reset`, `delete_demo`, the "elsewhere" guard and `write_media`'s folder guard cover it with no other change. Everything stays inside the same armed tripwires and one transaction. - **Notifications:** the copy's bell is a GM's: - transcript ready and failed, wiki suggestions, a member joined, ownership answered, safety waiting, and threads started; - plus `session_at_risk` and `audio_deletion_pending`, which only a GM gets. - Dana's bell is the owner's Amberwake player bell, re-addressed. **Deliberately identical:** transcripts and world text. So "Dana" still appears *inside* content (the table talk, and a few template lines). Only the engine's own attributions change. **Not mirrored:** - the Tuesday Tide's share of lanes 6 and 8; - the other three campaigns; - content notices; - the two admin audit events about Amberwake's pack. **Checks:** - **`test_demo_seed.py`:** 6 pass. The new mirror test covers: - the copy's name, owner and seats; - equal per-table counts across 59 tables (notifications +2, audit −2, as above); - GM-authored rows attributed to Dana in Amberwake and to the owner in the copy (a prep sheet, an approval, a reveal, a bot test); - `:::for` rendering for Dana and the GM, and not for Marcus; - the GM bell's kinds; - the round trip for `--reset`, and files in each campaign's own folder (76 each). - **The CLI** on a freshly migrated database: - the first run wrote 5 campaigns and 152 files; - a re-run said "already exists" and exited 0; - `--reset` rebuilt it; - `--dry-run` wrote nothing. - **The originals are unchanged:** main's seed and this branch's were compared row by row in fresh databases. Every main row is present, except for server `now()` timestamps and one existing random default (a quest page's `lore_entries.version_id`). The branch's extra rows are exactly the copy. - **Lint and changelog:** ruff check and format are clean, and `changelog.py check` passes. **On dev:** the next deploy re-runs the seed with `--reset`. The README says how. Changelog: `changelog.d/951-demo-gm-copy.added.md`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
WIP #951: README and changelog fragment for the GM copy
All checks were successful
CI / Docker image build (pull_request) Successful in 18s
CI / Bot/backend version sync (pull_request) Successful in 32s
CI / Backend lint (ruff) (pull_request) Successful in 34s
CI / Does this change touch the recording path? (pull_request) Successful in 37s
CI / Synthetic session harness (no GPU, no LLM) (pull_request) Has been skipped
CI / Feedback site tests (pull_request) Successful in 50s
CI / Summarisation accuracy eval harness (stub provider) (pull_request) Successful in 1m0s
CI / Bot tests and audit (pull_request) Successful in 1m48s
CI / Frontend (shard 2/2) (pull_request) Successful in 5m11s
CI / End-to-end journeys (keyboard) (pull_request) Successful in 6m49s
CI / Backend migration, tests, and audit (pull_request) Successful in 16m13s
CI / End-to-end journeys (phone) (pull_request) Successful in 15m52s
CI / End-to-end journeys (desktop) (pull_request) Successful in 17m27s
CI / Frontend (shard 1/2) (pull_request) Successful in 19m3s
c70e6ce822
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No description provided.