Files
code_of_conquest_dnd/docs/superpowers/specs/2026-07-12-character-reuse-seven-arc-design.md
Phillip Tarrant 021c2480d8 docs(spec): character reuse + the seven-campaign arc
A direction spec: one character, seven campaigns (one per demon king),
one saga — then it ends. The character, companions, deeds and level carry
between campaigns; the world does not.

Deliberately adds no milestone and no BOM line. Its real payload is a set
of guardrails on milestones that are about to be built blank and would
otherwise foreclose the shape:

- M5 owes a level curve — design it to a ceiling of 20 across 7 tiers,
  with tiered enemy stat blocks and never a scaling multiplier.
- M9 owes a save format — two objects (Saga + Campaign), not one blob.
  Humiliations move to the Saga or they die with the campaign.
- M3/M4 — don't foreclose a saga entry path; don't couple construct() to
  the creation scene.
- Build tool — add tier:1..7 when the item/quest namespaces land.

Difficulty is fixed at world-construction time from the campaign index,
never during play. That prohibition is the whole design; a world that
re-levels with the player is a treadmill.

The canon-log contract does not change: party[].disposition,
humiliations[] and established_facts[] already exist, and player still
admits no number. The Saga becomes the source; the canon log a projection
of it. The seam is one optional fourth arg to NewGame.construct().

Greywater at tier 1 under Ghaul is, unintentionally, already campaign 1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:16:50 -05:00

14 KiB
Raw Blame History

Character reuse & the seven-campaign arc — design

Date: 2026-07-12 Status: approved (direction); implementation = guardrails inside existing milestones, no new milestone work

Commits the shape of character reuse across campaigns, the seven-king meta-arc, and generation-time difficulty tiering — and names the cheap decisions that must land inside M3/M4/M5/M9 and the content build tool so the shape stays reachable. It authors no content, adds no BOM line, and does not unpark v2 world generation.


Problem

A character is born at level 1, plays one authored campaign, and stops. There is no structure that lets a character grow across worlds, and nothing in the roadmap forbids one — but several unbuilt milestones are about to make choices that would quietly foreclose it.

Specifically: M5 owes a level curve (the races/classes design says only "level curve → M5") and M9 owes a save format. Both are blank. If M5 picks an open-ended curve and M9 picks a single save blob, character reuse becomes a retrofit through the two most expensive systems in the game — the same class of mistake §10 warns about with the RNG seed.

This is a direction spec. Its deliverable is a shape plus a short list of constraints, not a feature.

Approach

One character, seven campaigns, one saga — then it ends.

Each campaign faces one of the Seven (content/lore/the-seven.md). The character, their companions, their deeds and their level carry from campaign to campaign; the world does not. Difficulty is chosen once, at world-construction time, from the campaign index — never during play.

Replayability lives at the character level, not the campaign level: a new character is a new saga, a new sequence of worlds, potentially a different order of kings. Seven campaigns is a finite, satisfying arc that a level curve can actually be designed against.

Rejected alternatives:

  • Kings as milestones, not chapters (campaigns not 1:1 with kings) — no natural level ceiling; "am I making progress" goes fuzzy.
  • Kings unkillable, truly infinite (canon-compatible: you only break a king's grip) — makes the level curve unsolvable, and the running joke turns bleak.
  • Full carry of gear and gold — the tier-N economy has nothing to sell a party hauling four campaigns of loot; §3's "counting coppers for an inn bed" grit dies.
  • Event-sourced saga (deeds are the log; the sheet is derived) — elegant, overkill.

1. The state model

The canon-log contract does not change. No schema bump, no API change, the proxy never notices. Every field this design needs already exists in the log — party[].disposition, humiliations[], established_facts[] — and player still admits only name / class_id / luck_descriptor, so the AI never sees a level. The §7 boundary holds for free.

What changes is who fills those fields.

Two objects

Saga — durable, survives campaigns. Its own schema, its own save file.

schema_version, saga_id
character:            name, race_id, calling_id, level (1..20),
                      stats {str, dex, con, fth, mag}, luck_base
companions:           [{id, approval}]              # Cadwyn, Brannoc
humiliations:         [{id, text, weight, turn}]    # STACKING across campaigns (§9)
deeds:                ["…"]                         # what the world knows you did
kings_slain:          ["ghaul_the_yoke", …]         # subset of the Seven
campaigns_completed:  0..7                          # tier = this + 1
signature_item_id:    string | null                 # the one thing that survives the Interlude

Campaign — per-campaign, discarded between them: CanonLog + GameState + the world content set + the RNG seed (§10). Exactly what M9's save/load was already going to hold.

Rejected: one save file with two sections (the boundary becomes a naming convention rather than a structure). The Saga is also the future input to world generation — tier, deeds, kings already slain, party composition — so it wants to be a standalone serializable object on its own merits.

Saga invariants

  • One campaign, one king: len(kings_slain) == campaigns_completed. The saga advances only by killing a king.
  • The saga is complete at campaigns_completed == 7. There is no campaign 8 and no tier 8. A complete saga is read-only: it can be viewed, not continued. That character is done, and the retirement finally takes.

The thesis

The Saga is the source. The canon log is a projection of the Saga into the current campaign.

This is §11's own rule one level up — the canon log is never the source of truth for the Saga, exactly as AI prose is never the source of truth for state. It gives every carried thing one uniform mechanism instead of five special cases:

Carried Projected into Note
Companion approval party[].disposition The Saga overrides the origin's disposition_overrides
Humiliations humiliations[] Move: they currently live in the log and would die with it
Deeds established_facts[] Appended alongside the origin's opening_facts
Level, stats, luck_base GameState Never the log — the AI sees no number (§7)

The seam

client/scripts/newgame/new_game.gd already reads three immutable inputs and writes two products. It gains one optional fourth input:

static func construct(origin, world, creation, rng, saga: Saga = null) -> Dictionary
  • saga == null → campaign 1. Roll stats, roll Luck, read dispositions from the origin. Today's behaviour, byte-for-byte unchanged.
  • saga != null → campaign N. Stats / level / luck_base come from the Saga instead of the dice; companion approval overrides the origin; deeds and humiliations are injected; the signature item is the only grant that is not the origin's.

One function, one new parameter, a null default that preserves every existing test. The character-creation UI is simply not shown on the saga path.

§2: state. The Saga is code-owned. The AI never reads or writes it.


2. Tier

tier = campaigns_completed + 1, an integer 1..7 (there is no tier 8 — see Saga invariants). It is an input to world construction, evaluated exactly once, at campaign start.

The hard rule

Difficulty is fixed at generation. Nothing scales during play.

This is the difference between the design and the Oblivion trap. Once the world is built at tier 5 it is fixed — this dungeon is deadly, that road is safe, and the gradient means something. A world that re-levels as the player levels is a treadmill, and it makes leveling meaningless. This is a prohibition, not a preference.

What tier selects:

  • Which enemies exist. Deterministic damage (§7) forbids multiplying a stat block, and that is a gift: a tier-6 world has different monsters, not a rat with 40× HP. The cosmology already supplies the ladder — proximity to a king is the power scale. Ghaul's tax collector, then his yoked, then a possessed magistrate, then one of the Sent, then Ghaul.
  • The level band. Tier N ≈ levels 3N2 … 3N, capped at 20. The exact numbers are M5's to tune; the bands are the guardrail.
  • Loot, shop stock, and the price table (§18: the loop is code, the numbers are content).
  • The king — one of the Seven, drawn from those not in kings_slain.
  • The story-shape. Tier 1 is a cult cell in a town. Tier 4 is a cult that owns a city. Tier 7 is the king himself.

Content-side cost: one field. Content entries (item, quest, enemy units) carry tier: 1..7, validated by the build tool. This is schema, not content — it does not violate the content-track spec's rule ("never author anything that is not in the Bill of Materials").

§2: state.


3. The Interlude

A state transition between campaigns: Interlude(saga, completed_campaign) → saga'.

In fiction: they retired, and it did not take. They thought campaign 1 was the end. Here they are at campaign 5, still at it. Brannoc keeps expecting to die on a job like this one and keeps not dying, and being dragged back out is both the joke and the tragedy — §9 deepened, not undercut.

Per §18 this is two systems: the transition is state (code owns it), the retirement-that-did-not-take is text (the DM narrates it, with an authored fallback per §13).

What it does:

  • Gold: mostly gone. Inventory: gone — except the one signature item the player chooses to keep.
  • Luck drift resets to luck_base. They slept in real beds for a year.
  • Deeds harvested from the finished campaign's canon log; kings_slain appended.
  • Humiliations merged, never overwritten (§9 is explicit) — now stacking across lives, not just hours.
  • campaigns_completed += 1.
  • Level, stats, companions, approval: untouched.

Two things make this more than bookkeeping.

It is an Improviser surface, already §7-compliant by construction. How they lost the money is a Luck-selected outcome. Bad luck: Cadwyn lost the purse at dice and there was a fire. Good luck: you kept the good armour. The Interlude's validator whitelist is exactly the §7 line — gear and coin are dignity; level, deeds, companions and the signature item are progress. It is structurally incapable of taking progress, which is the same guarantee §7 demands of the Improviser, reused verbatim.

The signature-item choice is the cursed-blade decision, seven times. §7 asks that "the decision to keep or discard a cursed item" be meaningful. You may keep exactly one thing turns the +4 STR / 5 LCK blade into a genuinely bad night's sleep.

§2: both — the transition is state, the interlude prose is text.


4. Guardrails — what lands in existing milestones

Nothing below adds a milestone or a BOM line. Each item constrains work already sequenced.

M3 — Title screen (unbuilt, next — cheapest possible moment)

A saga adds a third entry path alongside new-game / continue / load: begin the next campaign of an existing saga. Do not build it. Only avoid hardcoding the flow as new game → creation → world such that a fourth path cannot be added.

M4 — Character creation

NewGame.construct already takes creation as a plain Dictionary. Keep it that way: the creation scene produces that dict but must not become the only thing that can. On a saga path the dict is synthesized from the Saga and the UI is skipped. The guardrail is simply: do not couple construct to the creation scene.

M4 must emit race_id and calling_id into the creation dict (it will anyway) — they are Saga fields.

M5 — Tactical combat

  • Design the level curve to a ceiling of 20 across 7 tiers. The races/classes design currently says only "level curve → M5". It needs a number, and it needs to be this one, or campaign 7 discovers the math ran out.
  • Enemy stat blocks carry tier. There is no scaling multiplier, and there never will be. The M5 BOM line (1 enemy family + boss) is tier 1 — all the slice needs.
  • Class abilities are planned to L20, not open-ended.
  • This design depends on the damage-determinism reconcile already flagged for M5 (§7/§10 vs the Combat HUD mock's ranges). Tiering by stat block is what makes "no scaling" possible.

M9 — Save / load

  • Two objects, not one: Saga (durable) + Campaign (canon log + GameState + world + seed). This is the retrofit that genuinely hurts later — the §10 RNG-seed argument applied to persistence.
  • Humiliations move to the Saga, projected into the canon log. Today they live in the log and would die with the campaign.

Content build tool

When the item and quest namespaces land (already a sequenced M7/M8 prerequisite in the content-track design), add tier: 1..7 to the entry envelope, validated. One field, at a moment the model is already being opened.

Content roadmap

Mark each BOM line fixed substrate (currency, price table, abilities, callings, cosmology, the Seven, disposition ladder) vs per-campaign variable (town, NPCs, gated knowledge, quest chain, king). The distinction now has a concrete consumer.


5. The vertical slice is already campaign 1

Greywater Docks, tier 1, patron Ghaul the Yoke — exactly what the content-track design authored, before this spec existed. Zero change to that spec, zero change to the slice. The saga is what happens after it.


6. Testing

  • Regression gate: construct(saga = null) → every existing test passes byte-for-byte.
  • construct(saga = …) → level / stats / approval / humiliations / deeds project correctly; the Saga overrides the origin's disposition_overrides; only the signature item is granted.
  • The Interlude invariant, as a property test: level, deeds, companions and the signature item can never be reduced. This is the §7 Improviser whitelist expressed as an assertion.
  • The anti-Oblivion test: no code path reads tier after construction.
  • The saga invariants: len(kings_slain) == campaigns_completed; a Saga at campaigns_completed == 7 cannot begin a campaign.
  • Saga round-trips through save/load.

Consequences

  • M5 gains a hard level ceiling (20) and a tier model — it can no longer pick a curve that forecloses the saga.
  • M9 gains a two-object save format before any save exists in the wild.
  • Humiliations become durable — Brannoc acquires an anthology across lives, not just a repertoire across hours (§9).
  • The cosmology supplies the difficulty ladder for free — proximity to a king is the power scale, so no scaling multiplier is ever needed.
  • The Saga is the object v2 world generation will consume. That is the whole point of writing this now.
  • Difficulty scaling is structurally prohibited from becoming a treadmill.

Out of scope

  • AI world generation stays v2. This spec does not unpark it. It only guarantees the Saga is its input when it arrives.
  • Tiers 27 of content, the other six kings, and the seven story-shapes → Backlog.
  • Authoring any content at all. This spec builds a container and a set of prohibitions.