# Traits

> Schema and rules for creating traits

- Skill: `nikolaj-lat/traits` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add nikolaj-lat/traits`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nikolaj-lat/traits/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: nikolaj-lat (https://skillmd.com/u/nikolaj-lat)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/nikolaj-lat/traits

---


# Traits

Edit `tabs/traits.json`.

## Trait Categories

Traits must be organized into categories in `tabs/traits.json` under `traitCategories`.

| Field | Description |
|-------|-------------|
| `name` | Display name for the category |
| `maxSelections` | How many traits player can pick. 0 means none can be picked — don't use it. Applies to the whole category, subcategories included |
| `traits` | Array of trait keys in this category |
| `description` | Optional player-facing text explaining what the category represents |
| `subcategories` | Optional groups within the category: `{ name, traits, description? }`. In character creation, subcategory traits are listed first, then the ungrouped root `traits` |

Category patterns:
- **Single selection** (class/background): `maxSelections: 1`
- **Single selection** (species): `maxSelections: 1`
- **Multiple selection** (miscellaneous): `maxSelections: 3`


Category selection at character creation ignores `requirements` (level-up picks only). `unlockedBy` and `excludedBy` do constrain creation choices: see Trait Dependencies below.

## Required Fields

| Field | Requirement |
|-------|-------------|
| `name` | Must match object key exactly |
| `description` | What the trait is — shown to players at character creation (full paragraph) |
| `traitNarrativeEffects` | The narrator's reference for portraying the trait. Usually the same as `description`; elaborate further or add hidden aspects the player shouldn't see |
| `attributes` | Array of attribute modifiers (can be empty `[]`) |
| `skills` | Array of skill modifiers (can be empty `[]`) |
| `resources` | Array of resource modifiers (can be empty `[]`) |
| `startingItems` | Array of items granted (can be empty `[]`) |
| `abilities` | Array of ability names granted (can be empty `[]`) |
| `requirements` | Array of requirement objects (can be empty `[]`). Only gates level-up trait picks — ignored at character creation |
| `unlockedBy` | Prerequisites (OR logic): trait keys or story start ids. Affects level-up picks and character creation (see Trait Dependencies) |
| `excludedBy` | Conflicts: trait keys or story start ids. Affects level-up picks and character creation (see Trait Dependencies) |

## description and traitNarrativeEffects

For **non-species traits**:
- By default, `description` and `traitNarrativeEffects` carry the **same content** — write both as full paragraphs.
- `description` is shown to players at character creation. `traitNarrativeEffects` is the narrator's reference during play.
- Let them **diverge** only when: (a) the narrative effects hold hidden aspects the player shouldn't know, or (b) the description is a shorter summary and `traitNarrativeEffects` is the elaborated, fuller version.

Older configs used a field named `quirk` for this; it is auto-migrated to `traitNarrativeEffects` verbatim. Always write `traitNarrativeEffects` in new configs.

For **species traits**, see the Species Traits section below.

## Conditional Fields

| Field | When to Include |
|-------|-----------------|
| `attributes` (non-empty) | Class/background traits that affect core stats |
| `skills` (non-empty) | Traits that grant expertise or training |
| `resources` (non-empty) | Traits that affect resource pools |
| `startingItems` (non-empty) | Class/background traits with signature equipment 
| `abilities` (non-empty) | Traits that unlock special perks or powers |
| `requirements` (non-empty) | Traits in the level-up pool that should be gated behind stats, skills, other traits, or character level |
| `vulnerabilities` / `resistances` / `immunities` | Traits that should change how much typed damage the player takes in combat (e.g. a fire-elemental species immune to fire). Values must match `combatSettings.damageTypes` |

## Level-Up Trait Picks

Traits can be offered as level-up rewards via `progressionSettings.levelUpTraitPool` in `tabs/settings.json` (default cadence: 1 pick every 10 levels). When a pick is pending, the player chooses from pool traits they don't already have whose `unlockedBy`/`excludedBy` conditions pass and whose `requirements` are met; the chosen trait applies immediately, exactly like a starting trait. An empty pool means no picks are ever granted.

**Gotcha:** `requirements` does nothing at character creation, it only filters the level-up pick list. To keep a trait out of starting selection everywhere, leave it out of every trait category.

## Trait Dependencies (unlockedBy and excludedBy)

Both fields list trait keys or story start ids: a trait can require, or be blocked by, other traits or specific story starts. At character creation, categories are evaluated in `traitCategories` order after the story start is chosen. A story start dependency always takes effect; a trait dependency only constrains choices in categories after the referenced trait's own category. A dependency that is neither an earlier-category trait nor a story start leaves the trait unconditionally unlocked. Changing an earlier selection removes later selections that are no longer eligible. Both fields also filter level-up trait picks.

**Trigger-granted traits:** a trait added by a `player-traits` trigger effect applies its modifiers and abilities but does NOT grant its `startingItems`. Only permanent acquisition paths (character creation, level-up picks) hand out starting items. Trait bonuses are reconciled as net deltas, so swapping traits never double-applies a modifier.

## TraitModifier Format

All modifiers use the same structure:

```typescript
{ attribute: string, modifier: number }  // For attributes
{ skill: string, modifier: number }      // For skills
{ resource: string, modifier: number }   // For resources
```

The `modifier` is an additive value (positive or negative). Multiple traits stack.

## Point Cost Guidelines

Traits should be balanced around a point budget. Use these guidelines:

| Modifier Type | Point Cost |
|---------------|-------------------|
| Attribute | 1 point |
| Skill at modifier 0 | 1 point |
| Resource per 10 | 1 point |
| Starting item | 1 point |
| Ability | 1 points |

Every trait within a single traitCategory should be roughly equal. 

**Target values:**
- The `isHealth` resource should be set at roughly 100.
- Attributes should be at an average of 12, ranging from 0-20.

## startingItems Format

```typescript
{ item: string, quantity: number }
```

The `item` must reference a valid item key from `tabs/items.json`.

## requirements Format

Array of prerequisite checks. All must be met for the trait to appear as a level-up pick. Same format as ability requirements.

```typescript
{ type: 'skill', variable: 'skill name', amount: 3 }      // Skill level >= 3
{ type: 'attribute', variable: 'strength', amount: 14 }   // Attribute value >= 14
{ type: 'characterLevel', amount: 5 }                     // Character level >= 5 (no variable)
{ type: 'resource', variable: 'mana', amount: 50 }        // Resource max >= 50
{ type: 'trait', variable: 'fire affinity', amount: 1 }   // Has trait (amount ignored)
```

## Schema

```typescript
interface TraitCategory {
  name: string
  maxSelections: number
  traits: string[]
  description?: string
  subcategories?: TraitSubcategory[]
}

interface TraitSubcategory {
  name: string
  traits: string[]
  description?: string
}

interface Trait {
  name: string
  description: string
  traitNarrativeEffects: string
  attributes: Array<{attribute: string, modifier: number}>
  skills: Array<{skill: string, modifier: number}>
  resources: Array<{resource: string, modifier: number}>
  startingItems: Array<{item: string, quantity: number}>
  abilities: string[]
  requirements: TraitRequirement[]
  unlockedBy: string[]
  excludedBy: string[]
  vulnerabilities?: string[]
  resistances?: string[]
  immunities?: string[]
}

type TraitRequirement =
  | { type: 'resource' | 'attribute' | 'skill' | 'trait'; variable: string; amount: number }
  | { type: 'characterLevel'; amount: number }
```

## Species Traits

When creating a trait that represents a **species** (playable race):

1. Must have corresponding NPC Type and World Lore entries
2. The `description` contains lore paragraphs + skill blocks separated by `\n\n`
3. The `traitNarrativeEffects` contains lore paragraphs only (no skills)
4. Lore paragraphs are identical across NPC Type `description`, Trait `description`, and Trait `traitNarrativeEffects`
5. Must include exactly **3 skills** that work as both player skills AND NPC abilities
6. Skills should reflect innate species abilities or cultural training

See [Species Consistency Rules](../species-rules.md) for the full requirements.

## Reference

For detailed documentation, see [traits-reference.md](references/traits-reference.md).

