Techniques, traits, events, arc beats, programs, philosophies, upgrades, trainings, milestones and
campaign stages all live in typed files under src/content/. Adding the 49th technique is a data
change, never an engine change.
Everything is re-exported and indexed by src/content/index.ts. The sim never reaches into a
specific content module — it reads techniqueById, eventsByScope, beatsByChapter and friends.
Run bun run test after any content change: src/sim/content.test.ts checks that every
cross-reference resolves, every id is unique, and every effect magnitude is in range.
| Kind | Pattern | Example |
|---|---|---|
| Technique | <modality>_<snake_name> |
cbt_thought_record |
| Trait | trait_<snake_name> |
trait_night_owl |
| Event | ev_<scope>_<snake_name> |
ev_staff_corner_office |
| Arc beat | beat_<snake_name> |
beat_first_time_crying |
| Upgrade | up_<snake_name> |
up_group_room |
| Training | train_<modality>_<tier> |
train_emdr_2 |
| Milestone | ms_<snake_name> |
ms_first_goodbye |
| Campaign stage | camp_<n>_<snake_name> |
camp_3_outcomes_review |
Techniques are the cards the player picks mid-session, so they are the depth engine. A good one is situationally right and wrong, not globally strong.
{
id: 'dbt_opposite_action',
name: 'Opposite Action',
modality: 'dbt',
tier: 2,
blurb: 'Name the urge, then do the opposite of what it wants, on purpose.',
flavor: 'She went to the party. She hated forty minutes of it and stayed for the rest.',
focuses: ['build_skills'],
goodFor: ['depression', 'anxiety'],
poorFor: ['trauma'],
chapters: ['work', 'consolidation'],
minStability: 0.5,
effects: { progress: 1.15, resilience: 0.06, energy: 2, regression: 0.9 },
}Guidance that keeps the card pool honest:
- Every technique needs at least one
goodFor, and about half should have apoorFor— a bad pick has to be possible, and it has to be legible. - Restrict roughly a third by
chapters, and most byfocuses. A card that is fine everywhere is a card that never creates a decision. - Set
minStability(0.5–0.7) on genuinely activating work — exposure, trauma processing, chair work. The UI shows the client's current stability against it. - Potent
progressmust be paid for somewhere: energy, stability, orregressionabove 1. - Ranges:
progress0.8–1.45,quality−0.04…+0.09,rapport/stability/resilience−0.08…+0.10,energy0–5,regression0.5–1.5,breakthrough0–0.08. - Tier 3 cards are stronger but narrower.
- If you set
philosophy, the technique is granted to the whole practice when that philosophy is chosen — and is unreachable otherwise, so it must not be a training'sgrantstarget.
Then add it to the right TrainingDef.grants in trainings.ts, or it will never reach a
therapist.
{
id: 'ev_client_asks_to_end',
scope: 'client',
title: 'They think they are done',
body: '{client} says the thing that brought them in has stopped running the show...',
weight: 3,
chapters: ['consolidation'],
mood: 'warm',
choices: [
{
id: 'agree',
label: 'Agree, and plan a proper ending',
hint: 'Ends treatment sooner. A clean goodbye is worth reputation.',
effects: { clientProgress: 12, reputation: 2 },
outcome: 'You spend the hour on what they will do when it gets hard again.',
},
// …
],
}Rules the whole event set holds to:
- 2–4 choices, and every choice states its consequence in
hint. The hint must not lie or omit the main cost. This is a design commitment, not a style preference — the UI renders the hint directly beneath the label, and also renders a derived preview ofeffects. - Real dilemmas: time vs. money, this client vs. the waitlist, the rule vs. the person. If one option is obviously correct, the event is not doing its job.
- Use
requireson a choice to unlock a better path when the player has the resource, trait or standing for it. - Set
once: trueon story-heavy beats andminDayon anything that assumes an established practice. - Magnitudes: cash ±80–1500, reputation ±1–6, communityTrust ±1–8, clientRapport ±0.05–0.15, clientStability ±0.05–0.2, clientPatience ±5–25, therapistMorale ±3–12.
- Tokens
{client},{clientFirst},{therapist},{therapistFirst},{practice}are substituted at runtime.
scope determines when it can fire: client (after a session), staff / day / practice
(overnight), program (while that program runs), session (reserved for the technique beat).
Beats are the narrative spine — a sentence or two of a real person's life, plus a small
mechanical nudge. Keyed by chapter, optionally by conditions and severity band.
Keep effects small (stability ±0.05–0.18, rapport ±0.04–0.12, progress ±3–8) and let the writing
carry the weight. Rarer, more dramatic beats get lower weight. A beat can raise a full event via
event, and can add a comorbidity mid-treatment via effects.addComorbidity — which is how a
life event genuinely shifts what a client needs.
Warm, specific, humane; never clinical-cold and never twee. Clients are people, not diagnoses. Real modality vocabulary and real pacing wisdom — you do not process trauma with an unstable client. Anonymised dignity: clients are "A.M., 34" in the UI. Concrete images beat adjectives, and every line should still be worth reading the tenth time you see it.