> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grindxp.app/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Skill Trees

> How the skill tree is built, how XP flows, and how skills are structured.

The skill tree module lives in `packages/core/src/skill/tree.ts`.

## Data Model

Skills are stored flat in the `skills` table with a `parent_id` self-reference:

```sql theme={null}
skills (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  parent_id TEXT REFERENCES skills(id),
  xp INTEGER NOT NULL DEFAULT 0,
  level INTEGER NOT NULL DEFAULT 0,
  user_id TEXT NOT NULL
)
```

## Building the Tree

`buildSkillTree(skills: Skill[]): SkillNode[]` takes the flat list from the database and constructs a directed acyclic graph. Each node has a `children` array containing its direct descendants.

```typescript theme={null}
import { buildSkillTree } from "@grindxp/core/skill";

const tree = buildSkillTree(await skillRepo.list(userId));
// → SkillNode[] with nested children
```

Root nodes (skills without a parent) are the top-level entries.

## XP Routing

When a quest is completed, `collectSkillXp` determines how XP is distributed:

```typescript theme={null}
const updates = collectSkillXp(quest.skillTags, xpEarned);
// → [{ skillId, xpDelta }, ...]
```

XP is awarded to each tagged skill directly. Parent skills do not automatically receive child XP; each node is independent.

`updateSkillXp` then applies the updates and recalculates the level for each affected skill.

## Skill Repository

```typescript theme={null}
import { createSkillRepository } from "@grindxp/core/vault";

const repo = createSkillRepository(db);

// Create a skill
await repo.create({ name: "Strength", parentId: "fitness-skill-id" });

// List all
const skills = await repo.list(userId);

// Build the tree
const tree = buildSkillTree(skills);

// Update XP after quest completion (called internally by quest repo)
await repo.addXp(skillId, xpDelta);
```
