efa9a3e4 2026-05-28 Pavel Safronov | 1 | # Cypher System (CSRD) Migration Plan |
| 2 | |
| 3 | ## Background |
| 4 | |
4aedf3e4 2026-05-28 Pavel Safronov | 5 | The app migrated from D&D 5e to the Cypher System Reference Document (CSRD) — Monte Cook Games' open ruleset used in Numenera, The Strange, and others. The migration is complete. `SAVE_VERSION` is currently **3**. |
efa9a3e4 2026-05-28 Pavel Safronov | 6 | |
| 7 | Every phase that changes `gameState` shape must increment `SAVE_VERSION` in `public/index.html`. Old saves are automatically offered for export and discarded. |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## Source material |
| 12 | |
| 13 | All content is available under the [Cypher System Open License (CSOL)](https://www.montecookgames.com/cypher-system-open-license/) — free for digital use. |
| 14 | |
| 15 | | Source | URL | Used for | |
| 16 | | --- | --- | --- | |
| 17 | | Cypher-System-JSON-DB | github.com/Jon-Davis/Cypher-System-JSON-DB | **Data arrays** — types (with pool values), descriptors, foci, abilities, cyphers. 1.74 MB JSON; too large to bundle directly, so we extract just the fields we need into compact inline JS arrays during development. | |
| 18 | | cyphersrd.quest | cyphersrd.quest | **System prompt** — authoritative rules text for task resolution, damage track, effort, GM intrusions, advancement. We write a tight summary (~800 tokens) from this. | |
| 19 | | Obsidian CSRD Markdown | github.com/Obsidian-TTRPG-Community/Cypher-SRD-Markdown | Cross-reference during development. | |
| 20 | |
| 21 | **Workflow for data extraction:** download CSRD.json from JSON-DB, run a small local script to pull the specific fields needed (type name + starting pools + cypher limit + starting abilities summary; descriptor name + stat bonus; focus name + one-line summary + tier 1 ability), and paste the result as inline JS arrays in `index.html`. No build step needed. |
| 22 | |
| 23 | --- |
| 24 | |
| 25 | ## Phase 1 — Character foundation |
| 26 | |
| 27 | The biggest structural change. D&D's Race + Class becomes Cypher's **Descriptor + Type + Focus** sentence: *"I am a Clever Adept who Talks to Machines."* |
| 28 | |
| 29 | ### 1a — Data definitions |
| 30 | |
4aedf3e4 2026-05-28 Pavel Safronov | 31 | - [x] Replace `RACES` array with `DESCRIPTORS` (34 entries, each with pool bonus and starting skills) |
| 32 | - [x] Replace `CLASSES` array with `TYPES` (4 entries: Warrior, Adept, Explorer, Speaker — each with starting pools, Cypher Limit, starting abilities, and starting skills) |
| 33 | - [x] Add `FOCI` array (69 entries, each with settings tags, playstyle description, and Tier 1 ability) |
efa9a3e4 2026-05-28 Pavel Safronov | 34 | |
| 35 | ### 1b — New `gameState.character` shape |
| 36 | |
| 37 | Replace current `{ name, race, class, level, hp, maxHp, xp, gold, inventory }` with: |
| 38 | |
| 39 | ```js |
| 40 | { |
| 41 | name, descriptor, type, focus, |
| 42 | tier, // 1–6 |
| 43 | effort, // = tier |
| 44 | might: { pool, max, edge }, |
| 45 | speed: { pool, max, edge }, |
| 46 | intellect: { pool, max, edge }, |
| 47 | xp, |
| 48 | damageTrack, // 'hale' | 'impaired' | 'debilitated' |
| 49 | recoveryUsed, // 0–4, resets each full rest |
| 50 | cypherLimit, // from type (usually 2) |
| 51 | cyphers, // [{ name, level, effect }] |
| 52 | skills, // [{ name, rank: 'trained'|'specialized'|'inability' }] |
| 53 | abilities, // [{ name, cost, description }] — from type + focus |
| 54 | equipment, // string[] (replaces inventory) |
| 55 | shins, // currency (or 'gold' for generic settings) |
| 56 | } |
| 57 | ``` |
| 58 | |
| 59 | - [x] Update `beginAdventure()` to build this shape from Type + Descriptor + Focus selections |
| 60 | - [x] Bump `SAVE_VERSION` to 2 |
| 61 | |
| 62 | ### 1c — Creation screen |
| 63 | |
| 64 | - [x] Replace two-grid layout (Race + Class) with three-grid layout (Descriptor + Type + Focus) |
3207a0e0 2026-05-28 Pavel Safronov | 65 | - [x] The sentence "I am a `___` `___` who `___`" assembles live as user picks each |
efa9a3e4 2026-05-28 Pavel Safronov | 66 | - [x] Type cards show starting pool values and Cypher Limit, not HP |
| 67 | - [x] Focus cards show a one-line flavour summary |
| 68 | - [x] Descriptor cards show the key stat bonus it provides |
| 69 | |
| 70 | ### 1d — Header title format |
| 71 | |
| 72 | - [x] Update `header-title` from `"Name — Race Class"` to `"Name — Descriptor Type who Focus"` |
| 73 | |
| 74 | --- |
| 75 | |
| 76 | ## Phase 2 — Core mechanics |
| 77 | |
| 78 | ### 2a — New `UPDATE_TOOL` schema |
| 79 | |
| 80 | Replace current fields entirely: |
| 81 | |
3207a0e0 2026-05-28 Pavel Safronov | 82 | ```text |
efa9a3e4 2026-05-28 Pavel Safronov | 83 | might_damage // number — damage to Might pool |
| 84 | speed_damage // number — damage to Speed pool |
| 85 | intellect_damage // number — damage to Intellect pool |
| 86 | might_restore // number — points restored to Might pool |
| 87 | speed_restore // number — points restored to Speed pool |
| 88 | intellect_restore // number — points restored to Intellect pool |
| 89 | xp_gain // number |
| 90 | shins_change // number |
| 91 | cyphers_add // [{ name, level, effect }] |
| 92 | cyphers_remove // string[] — names of used/lost cyphers |
| 93 | abilities_add // [{ name, cost, description }] |
| 94 | equipment_add // string[] |
| 95 | equipment_remove // string[] |
| 96 | skills_add // [{ name, rank }] |
| 97 | tier_advance // boolean |
| 98 | recovery_used // boolean — mark one recovery slot as spent |
| 99 | reason // string |
| 100 | ``` |
| 101 | |
| 102 | ### 2b — New `applyStatUpdate()` logic |
| 103 | |
| 104 | - [x] Apply pool damage/restore, clamping to 0 and max |
| 105 | - [x] Recalculate `damageTrack` after every pool change: |
| 106 | - Hale → one pool at 0 → Impaired (−1 step to all rolls) |
| 107 | - Impaired → two pools at 0 → Debilitated (can barely act) |
| 108 | - Debilitated → all pools at 0 → Dead (end screen) |
| 109 | - [x] Enforce Cypher Limit: if `cyphers.length > cypherLimit`, warn player to use one |
| 110 | - [x] Handle `tier_advance`: increment `tier`, recalculate `effort = tier`, show tier-up fanfare |
1e438f26 2026-05-28 Pavel Safronov | 111 | - [x] Handle `recovery_used`: increment `recoveryUsed` (max 4; fourth resets all and marks 10-hour rest taken) |
efa9a3e4 2026-05-28 Pavel Safronov | 112 | - [x] Replace level-up toast with Tier-up toast (text: "TIER [N]!") |
| 113 | |
| 114 | ### 2c — System prompt rewrite ✅ |
| 115 | |
| 116 | Complete replacement of `buildSystemPrompt`. Claude needs to understand: |
| 117 | |
| 118 | - **Character sentence** — what Descriptor, Type, and Focus each contribute |
| 119 | - **Task difficulty** — 0–10 scale; target = difficulty × 3; player rolls d20 |
| 120 | - **Special roll results** — 1 = GM intrusion (free); 17 = minor effect; 18 = minor effect; 19 = major effect or +1 damage; 20 = major effect |
| 121 | - **Effort** — costs 3 from a pool per level (minus Edge); reduces difficulty by 1 per level applied |
| 122 | - **Assets** — each relevant asset reduces difficulty by 1 (max 2 from assets) |
| 123 | - **Skills** — trained = −1 difficulty; specialized = −2 difficulty; inability = +1 difficulty |
| 124 | - **Damage track** — Impaired penalty (−1 step = difficulty +1 to all rolls); Debilitated behavior |
| 125 | - **GM Intrusions** — Claude should offer them occasionally; player accepts (gains 2 XP, gives 1 away) or rejects (spends 1 XP); if player has no XP they must accept |
| 126 | - **Recovery rolls** — 1d6 + Tier; progression: 1 action → 10 min → 1 hour → 10 hours; after four the character must sleep to reset |
| 127 | - **Cyphers** — found during play; Claude should introduce them narratively; remind player when at Cypher Limit; one-use |
| 128 | - **XP flow** — earned via intrusions and discoveries; spent on advancement (4 per step) or short-term benefits (1 each: reroll, refuse intrusion, add +2 to any roll) |
| 129 | - **Advancement steps** (any 4 to advance a tier): increase pool max by 4; add +1 Edge to a stat; train a skill; other improvement |
| 130 | - **Stat tracking** — call `update_character` before narrative text, same as current |
| 131 | |
| 132 | --- |
| 133 | |
| 134 | ## Phase 3 — Sidebar UI redesign |
| 135 | |
| 136 | ### 3a — Three pool bars |
| 137 | |
| 138 | - [x] Replace HP bar with three bars: Might, Speed, Intellect |
| 139 | - [x] Each bar shows `current / max` and `Edge: N` |
| 140 | - [x] Bar colour shifts: green (full) → amber (half) → red (low) — same as current HP logic |
| 141 | - [x] Damage Track indicator below the bars: coloured badge — green "Hale" / amber "Impaired" / red "Debilitated" |
| 142 | |
| 143 | ### 3b — XP & advancement panel |
| 144 | |
| 145 | - [x] Replace XP progress bar (which tracked D&D level thresholds) with a plain XP counter |
| 146 | - [x] Tier shown as Roman numeral next to XP |
eabebeea 2026-05-28 Pavel Safronov | 147 | - [x] Show advancement progress: 4 dot pips below XP/Tier row |
4aedf3e4 2026-05-28 Pavel Safronov | 148 | - [x] "Spend 4 XP" button — disabled when XP < 4 or Tier 6; opens advancement modal (see Phase 5c); four steps advances the Tier and resets dots |
efa9a3e4 2026-05-28 Pavel Safronov | 149 | |
| 150 | ### 3c — Cyphers panel |
| 151 | |
| 152 | - [x] New sidebar section "Cyphers" (replaces or extends Inventory) |
| 153 | - [x] Each cypher shows name, level, effect summary |
eabebeea 2026-05-28 Pavel Safronov | 154 | - [x] "Use" button per cypher — removes it and sends activation message to Claude |
efa9a3e4 2026-05-28 Pavel Safronov | 155 | - [x] Cypher Limit displayed; section border turns orange when at limit |
| 156 | - [x] Equipment (non-cypher items) stays as a separate Inventory list |
| 157 | |
| 158 | ### 3d — Recovery roll button |
| 159 | |
eabebeea 2026-05-28 Pavel Safronov | 160 | - [x] Recovery section showing 4 slot pips (empty → filled as used) |
| 161 | - [x] "Recovery Roll (1d6 + Tier)" button — rolls and adds to most depleted pool |
| 162 | - [x] Marks one slot used per roll; when all 4 used button becomes "Full rest (reset slots)" |
| 163 | - [x] Clicking reset button clears all slots |
efa9a3e4 2026-05-28 Pavel Safronov | 164 | |
| 165 | --- |
| 166 | |
eabebeea 2026-05-28 Pavel Safronov | 167 | ## Phase 4 — Setting / genre selector ✅ |
efa9a3e4 2026-05-28 Pavel Safronov | 168 | |
| 169 | Cypher System is genre-neutral. Offer a setting choice at creation that adjusts tone, vocabulary, and available Foci. |
| 170 | |
eabebeea 2026-05-28 Pavel Safronov | 171 | - [x] Setting selector on creation screen: Fantasy / Numenera / Modern / Sci-Fi |
| 172 | - [x] Each setting customises: currency name, special abilities vocabulary, DM tone paragraph |
| 173 | - [x] System prompt includes setting context; currency label shown in sidebar |
| 174 | - [ ] Curated subset of Foci per setting (deferred — all foci remain available for now) |
efa9a3e4 2026-05-28 Pavel Safronov | 175 | |
| 176 | --- |
| 177 | |
4aedf3e4 2026-05-28 Pavel Safronov | 178 | ## Phase 5 — Skill system ✅ |
1b8688e1 2026-05-28 Pavel Safronov | 179 | |
4aedf3e4 2026-05-28 Pavel Safronov | 180 | The `skills` array in `gameState.character` is fully implemented: populated from Type and Descriptor starting skills, displayed in the sidebar with rank badges, managed via the advancement modal, and passed to Claude in the system prompt. |
1b8688e1 2026-05-28 Pavel Safronov | 181 | |
| 182 | ### 5a — Display |
| 183 | |
86441da6 2026-05-28 Pavel Safronov | 184 | - [x] New "Skills" section in the sidebar (between Abilities and Equipment) |
| 185 | - [x] Each skill shows name + rank badge: green "Trained" / gold "Specialized" / red "Inability" |
| 186 | - [x] Empty state: "No skills yet" |
1b8688e1 2026-05-28 Pavel Safronov | 187 | |
| 188 | ### 5b — Type and Descriptor starting skills |
| 189 | |
| 190 | Several Type Tier 1 abilities grant skill training ("Physical Skills", "Interaction Skills", "Danger Sense", etc.) but currently these are stored as narrative text in `abilities`, not as actual entries in `skills`. Fix by: |
| 191 | |
86441da6 2026-05-28 Pavel Safronov | 192 | - [x] Add a `startingSkills` field to each Type (parallel to `startingAbilities`) |
| 193 | - [x] Add `startingSkills` field to each Descriptor for their trained skills |
| 194 | - [x] Populate `character.skills` from both in `beginAdventure()` — overlapping trained skills promote to specialized |
1b8688e1 2026-05-28 Pavel Safronov | 195 | |
| 196 | ### 5c — Skill advancement |
| 197 | |
| 198 | The XP advancement path ("train a skill") is currently only narrated. To make it mechanical: |
| 199 | |
7aa0370d 2026-05-28 Pavel Safronov | 200 | - [x] Add a "Train a Skill" option to the Spend 4 XP flow — prompts for skill name, adds it as trained |
| 201 | - [x] Allow upgrading trained → specialized for another advancement step |
| 202 | - [x] Specialization cap at 2 levels; inability cannot be trained away (only reduced to trained via advancement) |
| 203 | - [x] Full advancement modal: pool +4, edge +1, train skill, specialize skill, other improvement |
1b8688e1 2026-05-28 Pavel Safronov | 204 | |
| 205 | ### 5d — Skill lookup in task resolution |
| 206 | |
| 207 | When a player's message mentions an action, Claude already references skills in its narration. Future enhancement: |
| 208 | |
86441da6 2026-05-28 Pavel Safronov | 209 | - [x] Pass `character.skills` in the system prompt so Claude can cite which skills apply to a given roll |
1b8688e1 2026-05-28 Pavel Safronov | 210 | - [ ] Expose "I am trained in X" as a quick action or structured input so the player can assert relevant skills before rolling |
| 211 | |
| 212 | --- |
| 213 | |
efa9a3e4 2026-05-28 Pavel Safronov | 214 | ## Notes |
| 215 | |
| 216 | - Phases 1–2 land together (one `SAVE_VERSION` bump to 2) — they're too intertwined to separate safely |
| 217 | - Phase 3 can ship independently after Phase 2 (UI only, no `gameState` shape change) |
| 218 | - Phase 4 is cosmetic and can go in any order |
86441da6 2026-05-28 Pavel Safronov | 219 | - Phase 5a/5b/5d landed together with `SAVE_VERSION` bump to 3 |
12d22f8f 2026-05-28 Pavel Safronov | 220 | - The CSRD is available at [cyphersrd.quest](https://cyphersrd.quest) for rules reference |