Interests Module
Location: apps/api/src/modules/interests/
Aggregate roots: Interest (catalog), UserInterest
Catalog Hierarchy
The interest catalog is a 3-level tree:
L1 — Category (e.g. Philosophy)
└── L2 — Sub-interest (e.g. Existentialism)
└── L3 — Deep interest (e.g. Heidegger's Being and Time)- L1 nodes have
parentId = nullandlevel = 1 - L2 nodes have
level = 2andparentIdpointing to L1 - L3 nodes have
level = 3andparentIdpointing to L2 - Each node has a
slug(URL-safe),name,path(dotted ltree-style), and optionalaccentColortoken
Accent Color Palette
| Token | Color |
|---|---|
ink | Dark charcoal |
gold | Amber |
sage | Green |
coral | Red-orange |
cyan | Teal |
plum | Purple |
Catalog Caching
The full catalog tree is cached in-process for 10 minutes. Both onboarding and the search screen hit this endpoint on every mount. Cache is invalidated on any interest create/update/delete (admin only).
User Interests
Each UserInterest row captures a user's engagement with one catalog entry.
Depth Scale
| Value | Label | Meaning |
|---|---|---|
| 1 | Curious | Just exploring |
| 2 | Reading | Actively learning |
| 3 | Practicing | Applying in practice |
| 4 | Deep | Advanced understanding |
| 5 | Lifework | Core to who you are |
Expertise Scale
Self-rated expertise (1–5) is distinct from depth. Depth = engagement level; expertise = perceived skill level. Both are stored independently.
Reputation Rollup
When reputation is recorded for an L3 interest, it rolls up to parents using these ratios:
| Level | Ratio |
|---|---|
| L3 (target) | 1.0 |
| L2 (parent) | 0.5 |
| L1 (grandparent) | 0.25 |
This means earning 10 rep in heidegger-being-and-time also adds 5 to existentialism and 2.5 (rounded) to philosophy.
For L2 targets:
| Level | Ratio |
|---|---|
| L2 (target) | 1.0 |
| L1 (parent) | 0.5 |
Events Emitted
| Event | Payload | Trigger |
|---|---|---|
interests.user_selected | {userId, interestId, depth} | User adds/changes interest |
interests.depth_changed | {userId, interestId, oldDepth, newDepth} | Depth update only |
API Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /interests/catalog | Full tree, 3 levels deep |
| GET | /interests/catalog?level=1 | Filter by level |
| GET | /interests/:idOrSlug | Single interest with children |
| GET | /me/interests | My selections with depth + expertise |
| POST | /me/interests | Add or update interest |
| PATCH | /me/interests/:id | Update depth/expertise/description |
| DELETE | /me/interests/:id | Remove selection |
Admin Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /admin/interests | Full list with user counts |
| POST | /admin/interests | Create catalog entry |
| PATCH | /admin/interests/:id | Update entry |
| DELETE | /admin/interests/:id | Soft-delete (clears isCustom, keeps rows) |
Onboarding Flow
During AI-assisted onboarding, interests are suggested by the AI reflection session via AssistantSuggestion. The user selects from suggestions; accepted suggestions are written to UserInterest via POST /ai/apply-suggestions.
The onboarding drill-down UI presents L1 → L2 → L3 in a three-step picker.