# Arete — Technical Specifications

> Personal development and life mastery domain. Named after the Ancient Greek
> concept of excellence, virtue, and living up to one's full potential.

This document specifies what is **implemented** in the Arete domain. Every
schema, field, enum value, table, endpoint, and configuration key below is
traceable to source code under `libs/arete/*`, `apps/arete/*`, or
`libs/contracts/src/arete/`.

The document is organized in layers matching the implementation: domain overview
and library inventory first, then the Zod validation schemas, then the database
schema (both legacy and V1 contract tables), then the API surface, events,
configuration, and integration points. A new engineer should read §1–2 for
orientation, then jump to whichever section matches the area they are working
on.

---

## 1. Domain Overview

The table below summarizes the domain's key properties at a glance. The `arete_`
table prefix is consistent across all 61 database tables and all enum types.

| Property          | Value                                                       |
| ----------------- | ----------------------------------------------------------- |
| Domain name       | `arete`                                                     |
| Scope             | Personal development and life mastery                       |
| Library count     | 12 Nx libraries (`libs/arete/*`)                            |
| Application count | 3 (`apps/arete/api`, `apps/arete/web`, `apps/arete/mobile`) |
| Database          | PostgreSQL via Drizzle ORM, `arete_` table prefix           |
| Language          | TypeScript (ESM)                                            |
| Validation        | Zod                                                         |
| ORM               | Drizzle ORM (`drizzle-orm` ^0.38.0)                         |
| API framework     | Fastify (`apps/arete/api`)                                  |

> **`libs/arete/database/`** contains only `prisma/schema.prisma` and a
> generated `prisma/generated/schema.sql`. It has no `package.json` or
> `project.json`, so it is **not** an Nx library and is not counted above. The
> authoritative schema for the domain is the Drizzle ORM schema in `@arete/core`
> (`src/db-schema.ts`).

---

## 2. Library Inventory

Twelve libraries carry a `package.json` and `project.json`. All are tagged
`["scope:arete", "layer:domain", "type:lib"]`. The source modules column lists
every module file under `src/` for each library.

| Package               | Path                      | Source modules                                                                                                                              |
| --------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `@arete/core`         | `libs/arete/core`         | `schemas`, `db-schema`, `db-seed`                                                                                                           |
| `@arete/habits`       | `libs/arete/habits`       | crud, habit-loop, four-laws, stacking, celebration, streaks, identity, keystone, analytics, reminders, recovery, friction, interventions    |
| `@arete/goals`        | `libs/arete/goals`        | crud, hierarchy, smart, okr, woop, twelve-week-year, progress, analytics                                                                    |
| `@arete/journal`      | `libs/arete/journal`      | crud, morning-pages, five-minute-journal, gratitude, thought-records, worry-journal, prompted-journal, reflection, analytics                |
| `@arete/time`         | `libs/arete/time`         | eisenhower, gtd-inbox, gtd-review, big-rocks, time-blocking, pomodoro, deep-work, daily-planning, time-audit                                |
| `@arete/vision`       | `libs/arete/vision`       | vision-board, mission-statement, values-clarification, ikigai, golden-circle, legacy                                                        |
| `@arete/balance`      | `libs/arete/balance`      | wheel-of-life, wellness-dimensions, perma, mood-tracking, sleep-tracking, energy-management, life-satisfaction                              |
| `@arete/seven-habits` | `libs/arete/seven-habits` | be-proactive, begin-with-end, put-first-things-first, think-win-win, seek-to-understand, synergize, sharpen-the-saw, emotional-bank-account |
| `@arete/affirmations` | `libs/arete/affirmations` | management, ai-affirmations                                                                                                                 |
| `@arete/gamification` | `libs/arete/gamification` | points, badges, levels, leaderboards, accountability, contracts, challenges, rewards                                                        |
| `@arete/ai-coach`     | `libs/arete/ai-coach`     | coaching, recommendations, patterns, weekly-review, coaching-summary-card, continuity-card, cross-domain, nlp-analytics, notifications      |
| `@arete/api-client`   | `libs/arete/api-client`   | `client`, `generated/openapi`                                                                                                               |

`@arete/core` carries runtime dependencies (`@oshun/contracts`, `drizzle-orm`,
`zod`). The eight personal-development feature libraries, `@arete/gamification`,
and `@arete/ai-coach` declare `@arete/core` as a `peerDependency` and carry no
other runtime dependencies. `@arete/api-client` is a standalone generated client
with no runtime dependency on `@arete/core`.

### 2.1 Applications

| App             | Path                | Stack                                                                   |
| --------------- | ------------------- | ----------------------------------------------------------------------- |
| `@arete/api`    | `apps/arete/api`    | Fastify REST API; JWT auth, Drizzle/`pg`, Redis, Swagger, nodemailer    |
| `@arete/web`    | `apps/arete/web`    | React + Vite, React Router, TanStack Query, Zustand, Tailwind, Recharts |
| `@arete/mobile` | `apps/arete/mobile` | React Native 0.73, React Navigation, TanStack Query, Zustand            |

`@arete/api` has the Nx tag set `["scope:arete", "type:app", "layer:service"]`.

---

## 3. Data Models — `@arete/core/schemas.ts` (56 Zod schemas)

`schemas.ts` exports **56** `*Schema` constants. Each schema exports an inferred
TypeScript type via `z.infer`. The numbered design comments in the file run 1–55
because two schemas (`MaturityStageSchema` enum and `GratitudeItemSchema` nested
object) are sub-schemas of larger entities.

Shared field helpers (`id`, `userId`, `datetime`, `dateOnly`, `timeOfDay`) are
defined once and reused. `id`/`userId` are `z.string().uuid()`; `datetime` is
`z.string().datetime()` (ISO 8601); `dateOnly` matches `YYYY-MM-DD`; `timeOfDay`
matches `HH:mm` (00:00–23:59).

The sub-sections below enumerate every schema in the file, grouped by the domain
entity they represent.

### 3.1 Identity and Profile

These schemas model the user and their defining personal context: who they are,
what they value, what roles they inhabit, and where they stand on Covey's
maturity continuum.

**`UserProfileSchema`** — user profile and preferences.

| Field       | Type                    | Notes                          |
| ----------- | ----------------------- | ------------------------------ |
| `id`        | uuid                    |                                |
| `email`     | email, ≤255             |                                |
| `name`      | string, 1–255           | display name                   |
| `avatar`    | url \| null             | default `null`                 |
| `timezone`  | string, 1–100           | IANA identifier, default `UTC` |
| `locale`    | string, 2–10            | default `en`                   |
| `createdAt` | datetime                |                                |
| `updatedAt` | datetime                |                                |
| `settings`  | record<string, unknown> | default `{}`                   |

**`PersonalMissionSchema`** — mission statement with associated values.

| Field          | Type                | Notes                          |
| -------------- | ------------------- | ------------------------------ |
| `id`           | uuid                |                                |
| `userId`       | uuid                |                                |
| `statement`    | string, 10–5000     | full mission statement         |
| `values`       | `ValueSchema[]`, ≥1 | core values supporting mission |
| `lastReviewed` | datetime \| null    |                                |
| `createdAt`    | datetime            |                                |

**`ValueSchema`** — `id`, `userId`, `name` (1–100), `description` (≤2000),
`priority` (int 1–10, 1 = highest), `category` (1–100).

**`RoleSchema`** — `id`, `userId`, `name` (1–100), `description` (≤2000),
`goals` (array of uuid, default `[]`), `isActive` (boolean, default `true`).

**`MaturityStageSchema`** — enum: `dependence`, `independence`,
`interdependence` (Covey's maturity continuum).

### 3.2 Goals

These schemas cover the full goal model: the base goal entity shared across all
five frameworks, plus the framework-specific extensions and sub-entities.

**`GoalSchema`** — base goal with hierarchical nesting.

| Field          | Type                | Notes                 |
| -------------- | ------------------- | --------------------- |
| `id`           | uuid                |                       |
| `userId`       | uuid                |                       |
| `title`        | string, 1–500       |                       |
| `description`  | string, ≤5000       | default `''`          |
| `type`         | `GoalTypeSchema`    |                       |
| `status`       | `GoalStatusSchema`  | default `not_started` |
| `startDate`    | date (`YYYY-MM-DD`) |                       |
| `targetDate`   | date (`YYYY-MM-DD`) |                       |
| `progress`     | number 0–100        | default `0`           |
| `parentGoalId` | uuid \| null        | default `null`        |

**`GoalTypeSchema`** — enum: `annual`, `quarterly`, `monthly`, `weekly`,
`daily`.

**`GoalStatusSchema`** — enum: `not_started`, `in_progress`, `completed`,
`abandoned`, `paused`.

**`SMARTGoalSchema`** — `GoalSchema` extended with `specific`, `measurable`,
`achievable`, `relevant`, `timeBound` (each string, 1–2000). These are stored as
additional columns on `arete_goals` rather than a separate table; see §5.1.

**`KeyResultSchema`** — `id`, `okrId` (uuid), `description` (1–1000),
`currentValue` (number), `targetValue` (number), `unit` (1–50), `score` (number
0.0–1.0).

**`OKRSchema`** — `id`, `userId`, `objective` (1–1000), `keyResults`
(`KeyResultSchema[]`, 1–5), `quarter` (int 1–4), `year` (int 2000–2100),
`status` (`GoalStatusSchema`, default `not_started`).

**`WOOPGoalSchema`** — `id`, `userId`, `wish` (1–1000), `outcome` (1–2000),
`obstacle` (1–2000, the main _internal_ obstacle), `plan` (1–2000, the if-then
plan), `status` (`GoalStatusSchema`). Like SMART criteria, WOOP fields are
columns on `arete_goals` rather than a separate table.

**`ImplementationIntentionSchema`** — `id`, `userId`, `goalId` (uuid),
`situation` (1–1000, the "if" trigger), `behavior` (1–1000, the "then" action),
`strength` (number 0–10).

### 3.3 Habits

The habit schemas model the full cue-routine-reward lifecycle, completion
records, streaks, and streak freezes. Note that the cue, routine, and reward
sub-structures are validated individually before being stored as JSONB in the
`arete_habits` table.

**`HabitSchema`** — a habit with cue-routine-reward loop and tracking stats.

| Field              | Type                   | Notes                         |
| ------------------ | ---------------------- | ----------------------------- |
| `id`               | uuid                   |                               |
| `userId`           | uuid                   |                               |
| `name`             | string, 1–200          |                               |
| `description`      | string, ≤2000          | default `''`                  |
| `category`         | string, 1–100          | e.g. `health`, `productivity` |
| `frequency`        | `HabitFrequencySchema` |                               |
| `cue`              | `HabitCueSchema`       |                               |
| `routine`          | `HabitRoutineSchema`   |                               |
| `reward`           | `HabitRewardSchema`    |                               |
| `isActive`         | boolean                | default `true`                |
| `currentStreak`    | int ≥0                 | default `0`                   |
| `bestStreak`       | int ≥0                 | default `0`                   |
| `totalCompletions` | int ≥0                 | default `0`                   |
| `createdAt`        | datetime               |                               |

**`HabitFrequencySchema`** — enum: `daily`, `weekly`, `specific_days`,
`flexible`, `x_per_week`.

**`HabitCueSchema`** — `type` (enum: `time`, `location`, `preceding_action`,
`emotional_state`), `value` (1–500), `description` (≤1000, default `''`).

**`HabitRoutineSchema`** — `steps` (array of 1–500-char strings, ≥1),
`starterStep` (1–500, the tiny first step), `durationMinutes` (int 1–480),
`difficulty` (int 1–5).

**`HabitRewardSchema`** — `type` (enum: `intrinsic`, `extrinsic`), `description`
(1–1000), `points` (int ≥0, default `0`).

**`HabitStackSchema`** — `id`, `userId`, `name` (1–200), `habits` (array of
uuid, ≥1, ordered), `triggerCue` (`HabitCueSchema`).

**`HabitCompletionSchema`** — `id`, `habitId` (uuid), `userId`, `date`
(`YYYY-MM-DD`), `completedAt` (datetime \| null), `notes` (≤2000, default `''`),
`quality` (int 1–5 \| null), `skipped` (boolean, default `false`), `skipReason`
(≤500 \| null).

**`StreakSchema`** — `id`, `habitId` (uuid), `userId`, `current` (int ≥0),
`best` (int ≥0), `startDate` (`YYYY-MM-DD`), `lastCompletionDate` (`YYYY-MM-DD`
\| null), `forgivenessDaysUsed` (int ≥0, default `0`), `forgivenessDaysAllowed`
(int ≥0, default `1`).

**`StreakFreezeSchema`** — `id`, `streakId` (uuid), `userId`, `date`
(`YYYY-MM-DD`), `reason` (≤500, default `''`), `wasAutomatic` (boolean, default
`false`).

### 3.4 Journal

The journal schemas cover the core entry record, prompts, templates, CBT thought
records, and gratitude entries. The `JournalEntrySchema` is the primary record;
the specialized schemas below are separate entities linked to it.

**`JournalEntrySchema`** — a journal entry with mood tracking and tagging.

| Field       | Type                  | Notes                       |
| ----------- | --------------------- | --------------------------- |
| `id`        | uuid                  |                             |
| `userId`    | uuid                  |                             |
| `title`     | string, 1–500         |                             |
| `content`   | string, 1–50000       | Markdown supported          |
| `mood`      | int 1–5 \| null       | 1 = very low, 5 = great     |
| `tags`      | array of strings 1–50 | default `[]`                |
| `template`  | string ≤100 \| null   | template used for the entry |
| `isPrivate` | boolean               | default `true`              |
| `wordCount` | int ≥0                |                             |
| `createdAt` | datetime              |                             |
| `updatedAt` | datetime              |                             |

**`JournalPromptSchema`** — `id`, `text` (5–1000), `category` (enum:
`gratitude`, `reflection`, `growth`, `creativity`, `mindfulness`), `isSystem`
(boolean, default `false`).

**`JournalTemplateSchema`** — `id`, `name` (1–200), `type` (enum: `five_minute`,
`morning_pages`, `evening_reflection`, `weekly_review`, `monthly_review`),
`prompts` (array of 1–1000-char strings, ≥1), `structure` (record<string,
unknown>, default `{}`).

**`ThoughtRecordSchema`** — a CBT thought record. The `emotionIntensity` and
`newEmotionIntensity` fields bracket the before/after mood shift produced by
completing the thought record exercise.

| Field                 | Type              | Notes                     |
| --------------------- | ----------------- | ------------------------- |
| `id`                  | uuid              |                           |
| `userId`              | uuid              |                           |
| `situation`           | string, 1–2000    | the triggering situation  |
| `automaticThought`    | string, 1–2000    |                           |
| `emotion`             | string, 1–100     | the primary emotion       |
| `emotionIntensity`    | int 0–100         |                           |
| `evidenceFor`         | string, ≤2000     | default `''`              |
| `evidenceAgainst`     | string, ≤2000     | default `''`              |
| `balancedThought`     | string, ≤2000     | default `''`              |
| `newEmotionIntensity` | int 0–100 \| null | intensity after reframing |
| `createdAt`           | datetime          |                           |

**`GratitudeItemSchema`** — `text` (1–500), `category` (1–100).

**`GratitudeEntrySchema`** — `id`, `userId`, `items` (`GratitudeItemSchema[]`,
1–10), `date` (`YYYY-MM-DD`).

### 3.5 Vision

The vision schemas model digital vision boards and the items placed on them. The
`VisionBoardCategorySchema` enum restricts board categories to the ten life
areas used throughout the domain; the item `type` field determines whether the
board cell contains an image, text, or a linked goal.

**`VisionBoardCategorySchema`** — enum: `career`, `health`, `relationships`,
`finances`, `personal_growth`, `spirituality`, `adventure`, `creativity`,
`family`, `education`.

**`VisionBoardItemSchema`** — `id`, `boardId` (uuid), `type` (enum: `image`,
`text`, `goal_link`), `content` (1–5000), `position` (`{ x, y }`, both ≥0),
`size` (`{ w, h }`, both ≥1), `goalId` (uuid \| null).

**`VisionBoardSchema`** — `id`, `userId`, `name` (1–200), `description` (≤2000),
`isActive` (boolean, default `true`), `items` (`VisionBoardItemSchema[]`,
default `[]`), `createdAt`.

### 3.6 Time Management

The time management schemas span both higher-level planning (weekly plans with
big rocks, daily plans with MIT trios) and execution-level records (Pomodoro
sessions, deep work sessions, and individual tasks in the Eisenhower matrix).

**`TaskQuadrantSchema`** — enum: `Q1_urgent_important`,
`Q2_not_urgent_important`, `Q3_urgent_not_important`,
`Q4_not_urgent_not_important` (Eisenhower matrix).

**`TaskSchema`** — `id`, `userId`, `title` (1–500), `description` (≤5000),
`quadrant` (`TaskQuadrantSchema`), `priority` (int 1–10, 1 = highest), `dueDate`
(`YYYY-MM-DD` \| null), `estimatedMinutes` (int 1–1440 \| null), `completedAt`
(datetime \| null), `isRecurring` (boolean, default `false`), `tags` (array of
strings 1–50, default `[]`).

**`BigRockSchema`** — `id`, `userId`, `weekStart` (`YYYY-MM-DD`, Monday),
`title` (1–500), `description` (≤2000), `roleId` (uuid \| null), `isCompleted`
(boolean, default `false`), `priority` (int 1–7).

**`TimeBlockSchema`** — `id`, `userId`, `date` (`YYYY-MM-DD`), `startTime`
(`HH:mm`), `endTime` (`HH:mm`), `title` (1–500), `category` (enum: `deep_work`,
`admin`, `meeting`, `personal`, `health`, `learning`), `isFlexible` (boolean,
default `false`).

**`WeeklyPlanSchema`** — `id`, `userId`, `weekStart` (`YYYY-MM-DD`), `bigRocks`
(`BigRockSchema[]`, default `[]`), `roles` (array of uuid), `review` (≤5000),
`sharpenTheSaw` (≤2000, planned renewal activities).

**`DailyPlanSchema`** — `id`, `userId`, `date` (`YYYY-MM-DD`), `bigThree` (array
of exactly 3 strings 1–500), `timeBlocks` (`TimeBlockSchema[]`, default `[]`),
`morningRoutine` (≤2000), `eveningReview` (≤2000).

**`PomodoroSessionSchema`** — `id`, `userId`, `taskId` (uuid \| null),
`startedAt` (datetime), `duration` (int 1–120, default `25`), `breakDuration`
(int 0–60, default `5`), `completed` (boolean, default `false`), `distractions`
(int ≥0, default `0`).

**`DeepWorkSessionSchema`** — `id`, `userId`, `startedAt` (datetime), `endedAt`
(datetime \| null), `focusScore` (int 0–100 \| null), `objective` (1–1000),
`accomplishments` (≤5000), `distractions` (int ≥0).

### 3.7 Seven Habits

These schemas directly model Covey's constructs: the Circle of Influence/Concern
classification, and the Emotional Bank Account with per-relationship transaction
histories.

**`CircleOfInfluenceSchema`** — `id`, `userId`, `item` (1–1000), `circle` (enum:
`concern`, `influence`), `actionable` (boolean, default `false`), `notes`
(≤2000).

**`EmotionalTransactionSchema`** — `id`, `accountId` (uuid), `type` (enum:
`deposit`, `withdrawal`), `description` (1–1000), `amount` (int 1–10,
magnitude), `category` (enum: `courtesy`, `kindness`, `honesty`, `commitment`,
`apology`, `forgiveness`), `date` (datetime).

**`EmotionalBankAccountSchema`** — `id`, `userId`, `relationshipName` (1–200),
`balance` (int, may be negative), `lastTransaction` (datetime \| null),
`transactions` (`EmotionalTransactionSchema[]`, default `[]`).

### 3.8 Balance and Wellness

The wellness schemas cover life balance assessment (Wheel of Life), renewal
activity planning, and daily biometric check-ins (mood, sleep, energy). The
`WellnessDimensionSchema` enum is the shared vocabulary for dimension types
across all three assessment tools.

**`WellnessDimensionSchema`** — enum: `physical`, `mental`, `emotional`,
`spiritual`, `social`, `financial`, `career`, `family`, `fun_recreation`,
`personal_growth`, `custom`.

**`WheelOfLifeDimensionSchema`** — `dimensionType` (`WellnessDimensionSchema`),
`customName` (1–80, optional), `customDescription` (≤500, optional), `rating`
(int 1–10), `targetRating` (int 1–10), `notes` (≤2000), `actions` (array of
strings 1–500, default `[]`). A `.refine()` enforces that `customName` is
present whenever `dimensionType` is `custom`.

**`WheelOfLifeSchema`** — `id`, `userId`, `dimensions`
(`WheelOfLifeDimensionSchema[]`, ≥1), `overallScore` (number 0–10, computed),
`assessmentDate` (`YYYY-MM-DD`), `notes` (≤5000).

**`RenewalActivitySchema`** — `id`, `userId`, `dimension`
(`WellnessDimensionSchema`), `activity` (1–500), `frequencyPerWeek` (int 1–14),
`durationMinutes` (int 1–480), `notes` (≤2000).

**`RenewalLogSchema`** — `id`, `activityId` (uuid), `userId`, `date`
(`YYYY-MM-DD`), `durationMinutes` (int 1–480), `quality` (int 1–5), `notes`
(≤2000).

**`MoodEntrySchema`** — `id`, `userId`, `mood` (int 1–5), `energy` (int 1–5),
`tags` (array of strings 1–50, default `[]`), `notes` (≤2000), `date`
(datetime).

### 3.9 Affirmations

**`AffirmationSchema`** — `id`, `userId`, `text` (1–1000), `category` (1–100),
`isActive` (boolean, default `true`), `frequency` (enum: `daily`, `weekly`,
`on_demand`, default `daily`), `lastReviewedAt` (datetime \| null).

### 3.10 Gamification

The gamification schemas model the three reward currencies (XP, coins, gems),
the badge and leveling system, accountability partnerships, commitment
contracts, and community challenges.

**`BadgeSchema`** — `id`, `name` (1–200), `description` (≤1000), `category`
(1–100), `icon` (1–200), `unlockCondition` (1–1000, human-readable), `points`
(int ≥0), `tier` (enum: `bronze`, `silver`, `gold`, `platinum`).

**`AchievementSchema`** — `id`, `userId`, `badgeId` (uuid), `earnedAt` (datetime
\| null), `progress` (number 0–100, default `0`).

**`LevelSchema`** — `level` (int 1–100), `title` (1–200), `xpRequired` (int ≥0),
`xpTotal` (int ≥0, cumulative), `perks` (array of strings 1–500, default `[]`).

**`PointsSchema`** — `id`, `userId`, `amount` (int ≥1), `type` (enum: `xp`,
`coins`, `gems`), `source` (1–500), `earnedAt` (datetime).

**`AccountabilityPartnerSchema`** — `id`, `userId`, `partnerUserId` (uuid),
`status` (enum: `pending`, `active`, `ended`, default `pending`), `sharedGoals`
(array of uuid, default `[]`), `createdAt`.

**`CommitmentContractSchema`** — `id`, `userId`, `goalId` (uuid), `stake`
(`{ description (1–1000), amount (≥0) }`), `referee` (uuid \| null), `deadline`
(`YYYY-MM-DD`), `status` (enum: `active`, `completed`, `failed`, default
`active`).

**`ChallengeSchema`** — `id`, `title` (1–500), `description` (1–5000),
`category` (1–100), `startDate` (`YYYY-MM-DD`), `endDate` (`YYYY-MM-DD`),
`participantCount` (int ≥0, default `0`), `rules` (array of strings 1–1000, ≥1),
`prizes` (array of strings 1–500, default `[]`).

---

## 4. V1 Contract Schemas — `@oshun/contracts/arete`

`libs/contracts/src/arete/index.ts` defines a second, newer schema family — the
"V1" contracts. These describe the humane-streak / friction-aware coaching
model: a more psychologically sensitive alternative to binary pass/fail habit
tracking. The `arete_v1_*` Drizzle tables (see §5.3) and `@arete/api-client`
types are typed against these contracts.

The V1 model introduces concepts not present in the legacy schemas: a declared
difficulty rating for each habit, a humane streak policy that distinguishes
between skip, decline, partial, and miss, friction signals from multiple Oshun
domains, and an intervention dispatch system that suggests corrective actions
when friction is detected.

### 4.1 Contract enums

The V1 contracts define 21 enums. The table below lists each schema and its
values. Note that some of these (e.g., `FrictionSignalSourceSchema`) name other
Oshun domains as valid signal sources — this is the primary cross-domain
coupling point in the Arete codebase.

| Schema                         | Values                                                                                                                                                                                                                                                 |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `CheckInStatusSchema`          | `done`, `partial`, `skip`, `decline`, `miss`                                                                                                                                                                                                           |
| `HabitCadenceKindSchema`       | (5 values; weekly / count-per-period / on-trigger among them)                                                                                                                                                                                          |
| `DeclaredDifficultySchema`     | `tiny`, `easy`, `moderate`, `hard`, `stretch`                                                                                                                                                                                                          |
| `GoalTimeframeSchema`          | `daily`, `weekly`, `monthly`, `quarterly`, `annual`, `multi-year`, `open-ended`                                                                                                                                                                        |
| `GoalScopeSchema`              | `identity`, `health`, `learning`, `work`, `relationship`, `spiritual`, `financial`, `community`, `creative`, `custom`                                                                                                                                  |
| `GoalStatusSchema` (V1)        | `not-started`, `active`, `paused`, `completed`, `abandoned`                                                                                                                                                                                            |
| `StreakTreatmentSchema`        | `engaged`, `grace`, `no-count`                                                                                                                                                                                                                         |
| `JournalEntryTypeSchema`       | `daily-reflection`, `weekly-review-note`, `cbt`, `gratitude`, `freeform`, `recovery`, `prompt-response`                                                                                                                                                |
| `WeeklyReviewStatusSchema`     | `scheduled`, `started`, `completed`, `skipped`                                                                                                                                                                                                         |
| `WeeklyReviewCadenceSchema`    | `sunday-evening`, `custom`                                                                                                                                                                                                                             |
| `PlanScopeSchema`              | `daily`, `weekly`, `recovery`, `goal-rescope`, `routine-adjustment`                                                                                                                                                                                    |
| `PlanStatusSchema`             | `draft`, `active`, `completed`, `superseded`, `archived`                                                                                                                                                                                               |
| `StreakVisualStateSchema`      | `steady`, `grace`, `drift`, `recovery`, `paused`                                                                                                                                                                                                       |
| `MissedDayDispositionSchema`   | `within-grace`, `logged-miss`, `recovered`, `excused`                                                                                                                                                                                                  |
| `FrictionSignalKindSchema`     | `time-of-day-mismatch`, `mood-incompatible-cadence`, `calendar-collision`, `cross-domain-cognitive-load`, `declared-sensitivity`, `location-friction`, `environment-unavailable`, `energy-drop`, `social-friction`, `streak-drift`, `over-scoped-plan` |
| `FrictionSignalSourceSchema`   | `check-in`, `journal`, `calendar`, `assistant`, `veritas`, `metis`, `tara`, `nyx`, `manual`                                                                                                                                                            |
| `FrictionSignalSeveritySchema` | `low`, `medium`, `high`                                                                                                                                                                                                                                |
| `InterventionKindSchema`       | `notification-retiming`, `plan-rescope`, `alternative-habit`, `accountability-check-in`, `breathwork-insert`, `tara-ritual-surfacing`, `weekly-review-prompt`, `routine-substitution`, `goal-criteria-clarification`                                   |
| `InterventionStatusSchema`     | `proposed`, `accepted`, `declined`, `applied`, `dismissed`, `expired`                                                                                                                                                                                  |
| `CoachingToneSchema`           | `invitational`, `direct`, `reflective`, `celebratory`, `recovery`                                                                                                                                                                                      |
| `RecoveryStageSchema`          | `invited`, `accepted`, `in-progress`, `completed`, `declined`, `expired`                                                                                                                                                                               |

### 4.2 Contract entities

The contract module exports thirteen entity schemas, each consumed by an
`arete_v1_*` table: `HabitSchema`, `RoutineSchema`, `GoalSchema`,
`CheckInSchema`, `MissedDaySchema`, `JournalEntrySchema`, `WeeklyReviewSchema`,
`PlanSchema`, `StreakRecordSchema`, `RecoveryRecordSchema`,
`FrictionSignalSchema`, `InterventionSchema`, `CoachingSummarySchema`.
Supporting nested schemas include `DeclaredCadenceSchema`,
`HumaneStreakPolicySchema`, `RoutineStepSchema`, `GoalCompletionSchema`,
`WeeklyReviewSectionSchema`, `PlanCommitmentSchema`, `StreakEventSchema`,
`MoodSnapshotSchema`, `AreteMetricSchema`, and `AreteReferenceSchema`.

Notable validation: `DeclaredCadenceSchema` uses `.superRefine()` to require
`daysOfWeek` for a `weekly` cadence, `targetCount` + `period` for
`count-per-period`, and `triggerRef` for `on-trigger`.
`HumaneStreakPolicySchema` carries `skipPreservesStreak`,
`declinePreservesStreak`, `missGraceCadences` (0–30),
`recoveryPromptAfterMisses` (1–30), and `visualLanguage` (`no-shame` or
`neutral`). The contract module also exports `CHECK_IN_STREAK_TREATMENT`, a
`Record<CheckInStatus, StreakTreatment>` mapping each check-in status to its
streak treatment.

---

## 5. Database Schema — `@arete/core/db-schema.ts`

`db-schema.ts` exports Drizzle ORM table definitions, relations, enums, and raw
SQL strings. `ALL_ARETE_TABLES` enumerates **61** tables; `ALL_ARETE_ENUMS`
enumerates **39** `pgEnum` definitions (`coachMessageRoleEnum` is a 40th enum,
defined inline and used by `arete_coach_messages` but not added to that array).
All tables use the `arete_` name prefix.

### 5.1 Legacy table groups

The 48 legacy tables are organized into eleven functional groups. A few
structural decisions are worth noting before reading the full list:

- **SMART and WOOP are columns, not tables.** `arete_goals` carries
  `smart_specific`, `smart_measurable`, `smart_achievable`, `smart_relevant`,
  `smart_time_bound`, and `woop_wish`, `woop_outcome`, `woop_obstacle`,
  `woop_plan` directly. There are no separate `arete_smart_goals` or
  `arete_woop_goals` tables.
- **Goal hierarchy is modelled twice**: a self-referential `parent_goal_id` on
  `arete_goals`, plus an explicit `arete_goal_hierarchy` join table with a
  unique `(parent_goal_id, child_goal_id)` index.
- **Credentials are separated** from `arete_users`: `arete_user_credentials`
  holds the bcrypt `password_hash` so the profile table can be `SELECT *`'d
  safely. `arete_password_reset_tokens` holds single-use, time-bounded reset
  tokens.
- **Habit `cue` / `routine` / `reward`** are stored as JSONB columns on
  `arete_habits`, not separate tables.
- **Mood, sleep, energy** are separate tables (`arete_mood_entries`,
  `arete_sleep_entries`, `arete_energy_entries`), each with a `sequence` column
  so multiple same-day entries are ordered.
- **`arete_habit_completions`** carries both the legacy fields and V1 check-in
  fields: `status` (`arete_v1_check_in_status`), `engagement_percent`,
  `status_reason`, `streak_treatment` (`arete_v1_streak_treatment`).
- **`arete_partner_invitations`** records outgoing partnership invites by email
  (a partner need not have an existing account).

| Group        | Tables                                                                                                                                                                                                                                     |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Identity     | `arete_users`, `arete_user_credentials`, `arete_password_reset_tokens`, `arete_personal_missions`, `arete_values`, `arete_roles`                                                                                                           |
| Goals        | `arete_goals`, `arete_goal_hierarchy`, `arete_okrs`, `arete_key_results`                                                                                                                                                                   |
| Habits       | `arete_habits`, `arete_habit_completions`, `arete_habit_stacks`, `arete_streaks`, `arete_streak_freezes`                                                                                                                                   |
| Journal      | `arete_journal_entries`, `arete_journal_prompts`, `arete_thought_records`, `arete_gratitude_entries`                                                                                                                                       |
| Vision       | `arete_vision_boards`, `arete_vision_board_items`                                                                                                                                                                                          |
| Time         | `arete_tasks`, `arete_weekly_plans`, `arete_daily_plans`, `arete_time_blocks`, `arete_pomodoro_sessions`, `arete_deep_work_sessions`                                                                                                       |
| Seven Habits | `arete_circle_items`, `arete_relationships`, `arete_emotional_transactions`                                                                                                                                                                |
| Balance      | `arete_wheel_of_life_assessments`, `arete_wellness_logs`, `arete_renewal_activities`, `arete_renewal_logs`, `arete_mood_entries`, `arete_sleep_entries`, `arete_energy_entries`                                                            |
| Affirmations | `arete_affirmations`                                                                                                                                                                                                                       |
| Gamification | `arete_achievements`, `arete_badges`, `arete_user_levels`, `arete_points_transactions`, `arete_accountability_partnerships`, `arete_partner_invitations`, `arete_commitment_contracts`, `arete_challenges`, `arete_challenge_participants` |
| Coaching     | `arete_coach_conversations`, `arete_coach_messages`, `arete_coach_feedback`                                                                                                                                                                |

### 5.2 Legacy enums (subset)

The following PostgreSQL enum types are defined in `ALL_ARETE_ENUMS`, all
prefixed `arete_` in the database:

`maturity_stage`, `goal_type`, `goal_status`, `habit_frequency`,
`journal_prompt_category`, `vision_board_item_type`, `task_quadrant`,
`time_block_category`, `circle`, `emotional_transaction_type`,
`emotional_transaction_category`, `wellness_dimension`, `affirmation_frequency`,
`badge_tier`, `points_type`, `partnership_status`, `contract_status`,
`challenge_participant_status`, `coach_message_role`.

### 5.3 V1 contract-backed tables

Thirteen `arete_v1_*` tables store the V1 contract entities. Their column lists
are exported as `ARETE_CONTRACT_RECORD_COLUMNS` and the table objects as
`ARETE_CONTRACT_RECORD_TABLES` (keyed `Habit`, `Goal`, `Routine`, `CheckIn`,
`Journal`, `WeeklyReview`, `Plan`, `CoachingSummary`, `Streak`, `MissedDay`,
`Friction`, `Intervention`, `Recovery`).

| Table                         | Stores                                                            |
| ----------------------------- | ----------------------------------------------------------------- |
| `arete_v1_habits`             | declared intent, difficulty, cadence, humane streak policy        |
| `arete_v1_goals`              | timeframe, scope, measurable criteria, sub-goals, completion      |
| `arete_v1_routines`           | declared cadence, time-of-day window, steps, substitution rules   |
| `arete_v1_check_ins`          | status, engagement %, reflection, mood/energy, friction notes     |
| `arete_v1_journal_entries`    | entry type, body markdown, linked habits/goals/check-ins          |
| `arete_v1_weekly_reviews`     | sections, inputs, plan adjustments, next-practice recommendations |
| `arete_v1_plans`              | scope, status, commitments, linked entities, recovery linkage     |
| `arete_v1_coaching_summaries` | tone, celebrated wins, observed patterns, suggested adjustments   |
| `arete_v1_streak_records`     | engagement counts, grace window, visual state, humane policy      |
| `arete_v1_missed_days`        | disposition, grace expiry, friction-signal links, recovery link   |
| `arete_v1_friction_signals`   | kind, source, severity, evidence refs, suggested interventions    |
| `arete_v1_interventions`      | kind, status, triggering signals, target, recommendation          |
| `arete_v1_recovery_records`   | stage, target, trigger, invitation, linked plan/interventions     |

The V1 enums (`arete_v1_*`, 20 enum types) mirror the contract enums in §4.1 and
are listed in §4.1's value table.

### 5.4 Relations

`ALL_ARETE_RELATIONS` exports Drizzle `relations()` definitions covering 55
tables. `areteUsers` is the hub: it declares `many`/`one` relations to missions,
values, roles, goals, OKRs, habits, completions, streaks, journal entries,
vision boards, tasks, plans, wellness logs, mood/sleep/energy entries,
affirmations, achievements, the user level, points transactions, partnerships
(both directions), commitment contracts (owner and referee), challenge
participations, coach conversations/feedback, and all thirteen V1 record types.
Foreign keys use `onDelete: 'cascade'` for owned data and `onDelete: 'set null'`
for soft references (e.g. `arete_vision_board_items.goal_id`,
`arete_pomodoro_sessions.task_id`, `arete_commitment_contracts.referee`).

### 5.5 Database automation (raw SQL exports)

Three SQL blocks are exported as strings from `db-schema.ts` and are intended to
be applied via migration. They encode the streak trigger, aggregation functions,
and full-text search indexes respectively.

**`STREAK_CALCULATION_TRIGGER_SQL`** — defines the
`arete_update_streak_on_completion()` PL/pgSQL function and the
`arete_streak_update_trigger` (`AFTER INSERT ON arete_habit_completions`,
`FOR EACH ROW`). The function logic proceeds as follows:

1. Skip rows where `skipped = true` — a recorded skip does not affect the
   streak.
2. Create a streak row for the habit if none exists yet.
3. Compute the day gap between the current insertion date and
   `last_completion_date`.
4. If the gap is 1 day (consecutive), increment `current` by 1.
5. If the gap is within the forgiveness window, consume a forgiveness day and
   increment.
6. If the gap exceeds the forgiveness window, reset `current` to 1.
7. Update `best` if `current` now exceeds it.
8. Write the new values back to both `arete_streaks` and the denormalized fields
   on `arete_habits` (`current_streak`, `best_streak`, `total_completions`).

**`STATISTICS_AGGREGATION_SQL`** — defines nine `STABLE` PL/pgSQL functions.
Each function is optimized for the dashboard and coach aggregation queries:

| Function                                        | Returns                                              |
| ----------------------------------------------- | ---------------------------------------------------- |
| `arete_total_completions(user, start?, end?)`   | count of non-skipped habit completions               |
| `arete_average_mood(user, start?, end?)`        | average `mood` from `arete_mood_entries`             |
| `arete_average_energy(user, start?, end?)`      | average `energy` from `arete_mood_entries`           |
| `arete_goal_completion_rate(user)`              | completed / total goals as a 0–100 percentage        |
| `arete_total_xp(user)`                          | sum of `xp`-type points transactions                 |
| `arete_habit_completion_rate(habit, days = 30)` | completions over N days as a 0–100 percentage        |
| `arete_journal_stats(user)`                     | OUT params: entry count, avg word count, total words |
| `arete_wheel_of_life_trend(user)`               | OUT params: current score, previous score, trend     |
| `arete_deep_work_hours(user, start?, end?)`     | total deep-work hours (sum of session durations)     |

**`JOURNAL_FULLTEXT_INDEX_SQL`** — creates two GIN indexes,
`arete_journal_entries_content_fts_idx` and
`arete_journal_entries_title_fts_idx`, both over `to_tsvector('english', …)`.

### 5.6 Seed data — `@arete/core/db-seed.ts`

`db-seed.ts` ships three sets of default data for initial database population.
Each export ships its own `Seed*` interface (`SeedJournalPrompt`, `SeedBadge`,
`SeedLevel`).

| Export                    | Contents                                                    |
| ------------------------- | ----------------------------------------------------------- |
| `DEFAULT_JOURNAL_PROMPTS` | 23 system journal prompts across the five prompt categories |
| `DEFAULT_BADGES`          | 17 badge definitions                                        |
| `DEFAULT_LEVELS`          | 10 level definitions                                        |

---

## 6. API Surface — `apps/arete/api`

The API is a Fastify application. `buildServer()` (`src/app.ts`) assembles the
instance; `server.ts` starts it. Routes are registered by `registerRoutes()`
(`src/routes/index.ts`): health probes are unversioned; everything else is
mounted under the `/v1` prefix.

### 6.1 Plugins

`src/plugins/` registers plugins in the following order, each decorating the
Fastify instance with domain-specific capabilities:

1. `request-context` — attaches per-request context for tracing and correlation
   IDs.
2. `cors` — configures cross-origin request handling.
3. `error-handler` — normalizes error responses to a consistent JSON shape.
4. `swagger` — serves OpenAPI documentation at `/documentation`.
5. `auth` — decorates `fastify.authenticate` using `@fastify/jwt`.
6. `database` — connects the `pg` pool and initializes the Drizzle client.
7. `redis` — initializes the `ioredis` client.
8. `rate-limit` — applies per-IP rate limiting.
9. `mailer` — configures `nodemailer` for transactional email.
10. `repositories` — registers per-domain repository modules (`users`, `habits`,
    `goals`, `journal`, `vision`, `time`, `balance`, `gamification`, `coach`).

### 6.2 Health routes (`/health*`, no auth)

`GET /health`, `GET /health/ready`, `GET /health/startup`.

### 6.3 Users — `/v1/users`

`POST /register`, `POST /login`, `POST /logout`, `GET /me`, `PATCH /me`,
`PATCH /preferences`, `GET /me/export`, `DELETE /me`, `POST /forgot-password`,
`POST /reset-password`, `POST /change-password`.

### 6.4 Habits — `/v1/habits`

`GET /`, `POST /`, `GET /today`, `GET /analytics`, `GET /:id`, `PATCH /:id`,
`DELETE /:id`, `GET /:id/analytics`, `POST /:id/complete`,
`GET /:id/completions`, `GET /:id/streak`, `GET /:id/recovery`,
`POST /:id/friction/analyze`, `POST /:id/interventions/dispatch`,
`POST /:id/stack`.

The friction-analysis and intervention-dispatch endpoints are the V1 contract
API — they operate on `arete_v1_*` tables and accept/return V1 contract schema
types.

### 6.5 Goals — `/v1/goals`

`GET /`, `POST /`, `GET /analytics`, `GET /okrs`, `POST /okrs`, `GET /:id`,
`PATCH /:id`, `DELETE /:id`, `POST /:id/progress`, `GET /:id/hierarchy`.

### 6.6 Journal — `/v1/journal`

`GET /entries`, `POST /entries`, `GET /entries/:id`, `PATCH /entries/:id`,
`DELETE /entries/:id`, `GET /prompts`, `GET /templates`, `GET /analytics`.

### 6.7 Vision — `/v1/vision`

`GET /boards`, `POST /boards`, `GET /boards/:id`, `PATCH /boards/:id`,
`DELETE /boards/:id`, `POST /boards/:id/items`, `GET /mission`, `PUT /mission`,
`GET /values`, `PUT /values`.

### 6.8 Time — `/v1/time`

`GET /tasks`, `POST /tasks`, `PATCH /tasks/:id`, `DELETE /tasks/:id`,
`GET /weekly-plan`, `PUT /weekly-plan`, `GET /daily-plan/:date`,
`PUT /daily-plan/:date`, `GET /pomodoro`, `POST /pomodoro/start`,
`POST /pomodoro/complete`.

### 6.9 Balance — `/v1/balance`

`GET /wheel-of-life`, `POST /wheel-of-life`, `GET /wheel-of-life/history`,
`GET /mood`, `POST /mood`, `GET /mood/analytics`, `GET /sleep`, `POST /sleep`,
`GET /energy`, `POST /energy`.

### 6.10 Gamification — `/v1/gamification`

`GET /points`, `GET /points/transactions`, `GET /badges`,
`GET /badges/available`, `GET /level`, `GET /leaderboards`, `GET /challenges`,
`POST /challenges/:id/join`, `GET /accountability-partners`,
`POST /accountability-partners/invite`.

### 6.11 Coach — `/v1/coach`

`POST /chat`, `POST /chat/stream` (SSE), `GET /conversations`,
`GET /recommendations`, `GET /insights`, `POST /feedback`. The stream endpoint
emits tokens at `COACH_STREAM_INTERVAL_MS` intervals using Server-Sent Events.

### 6.12 Dashboard — `/v1/dashboard`

`GET /summary`, `GET /weekly-activity`, `GET /recent-moods`, `POST /mood`,
`GET /profile-stats`.

### 6.13 API metadata

`GET /v1/info` (authenticated) returns
`{ name, version, description, environment, domains }`, where `domains` lists
`habits`, `goals`, `journal`, `vision`, `time`, `balance`, `gamification`,
`coach`, `affirmations`, `seven-habits`.

---

## 7. Events

The Arete domain does **not** publish a domain-event stream. There is no event
bus, no `emit`/`publish` infrastructure, and no event-name constants in
`libs/arete/*` or `apps/arete/*`. Cross-feature behaviour that would in other
systems be event-driven (streak recalculation on completion) is instead handled
synchronously by the PostgreSQL trigger in §5.5, and aggregation across features
is handled by the dashboard and coach routes querying the database directly.

---

## 8. Configuration

### 8.1 API environment variables — `apps/arete/api/src/config.ts`

`loadConfig()` parses `process.env` with a Zod schema; missing values fall back
to development defaults. A `superRefine` rejects the default `JWT_SECRET` in
production and rejects `DB_POOL_MIN > DB_POOL_MAX`.

| Variable                   | Default                                                 | Notes                             |
| -------------------------- | ------------------------------------------------------- | --------------------------------- |
| `PORT`                     | `3020`                                                  | 1–65535                           |
| `HOST`                     | `127.0.0.1`                                             |                                   |
| `NODE_ENV`                 | `development`                                           | `development`/`production`/`test` |
| `JWT_SECRET`               | `arete-dev-secret-change-me`                            | must be overridden in production  |
| `JWT_ISSUER`               | `arete-api`                                             |                                   |
| `JWT_ACCESS_TOKEN_TTL`     | `900` (s)                                               |                                   |
| `JWT_REFRESH_TOKEN_TTL`    | `604800` (s)                                            |                                   |
| `DATABASE_URL`             | `postgresql://oshun:oshun_dev@localhost:5432/oshun_dev` |                                   |
| `DB_POOL_MIN`              | `2`                                                     |                                   |
| `DB_POOL_MAX`              | `10`                                                    |                                   |
| `REDIS_URL`                | `redis://localhost:6379`                                |                                   |
| `RATE_LIMIT_MAX`           | `100`                                                   |                                   |
| `RATE_LIMIT_WINDOW_MS`     | `60000`                                                 |                                   |
| `LOG_LEVEL`                | `info`                                                  | Pino levels                       |
| `METRICS_PORT`             | `9090`                                                  |                                   |
| `CORS_ORIGIN`              | `*`                                                     |                                   |
| `SMTP_HOST`                | `127.0.0.1`                                             | Mailpit dev container             |
| `SMTP_PORT`                | `1025`                                                  |                                   |
| `SMTP_SECURE`              | `false`                                                 |                                   |
| `SMTP_USER` / `SMTP_PASS`  | (optional)                                              |                                   |
| `SMTP_FROM`                | `noreply@arete.local`                                   |                                   |
| `WEB_URL`                  | `http://localhost:5173`                                 | used in password-reset links      |
| `COACH_STREAM_INTERVAL_MS` | `50`                                                    | per-token SSE delay, 0–2000       |

### 8.2 Library build configuration

- Project tags (all libraries): `["scope:arete", "layer:domain", "type:lib"]`
- Module format: ESM (`"type": "module"`)
- Library build executor: `@nx/js:tsc`
- Library test executor: `@nx/vite:test` (per-library `vitest.config.ts`)
- `@arete/api` build/test/dev run via the `nx:run-commands` executor (`tsc`,
  `vitest run`, `tsx watch src/server.ts`)

---

## 9. Integration Points

### 9.1 Internal dependencies

The dependency boundaries are strict: no feature library imports another feature
library. All cross-feature data access flows through the application layer or
the database.

- Every personal-development feature library plus `@arete/gamification` and
  `@arete/ai-coach` depends on `@arete/core` (declared as a `peerDependency`).
- `@arete/api` depends on `@arete/core`, `@arete/habits`, `@arete/ai-coach`, and
  shared Oshun libraries (`@oshun/auth`, `@oshun/errors`, `@oshun/logging`,
  `@oshun/metrics`, `@oshun/tracing`).
- `@arete/web` depends on `@arete/habits`.
- `@arete/core` depends on `@oshun/contracts` for the V1 contract types used by
  the `arete_v1_*` tables.

### 9.2 Cross-domain references

Arete couples to other Oshun domains at a single, well-defined point: the V1
friction-signal and intervention system. The V1 `FrictionSignalSourceSchema`
names `veritas`, `metis`, `tara`, and `nyx` as domains that can publish friction
signals into Arete's habit engine. The V1 `InterventionKindSchema` includes
`tara-ritual-surfacing` and `breathwork-insert` as intervention types that
surface practices from the Tara domain. Finally, `@arete/habits/recovery`
exports a `DEFAULT_TARA_RECOVERY_PRACTICE_REF` constant so that Arete recovery
prompts can surface a specific Tara practice. These are the only cross-domain
couplings expressed in code — all other domain relationships described in the
architecture document are conceptual, not code-level.

---

## 10. Acceptance Criteria

A change to the Arete domain is consistent with this specification when all of
the following conditions hold:

1. New entities are added as Zod schemas in `@arete/core/schemas.ts` (or as V1
   contract schemas in `@oshun/contracts/arete`), with inferred types exported.
2. New persistent entities have a corresponding Drizzle table in `db-schema.ts`,
   registered in `ALL_ARETE_TABLES`, with relations registered in
   `ALL_ARETE_RELATIONS` and any new enum in `ALL_ARETE_ENUMS`.
3. New tables use the `arete_` prefix and `onDelete: 'cascade'` for owned data.
4. New API endpoints are registered through `registerRoutes()` under `/v1`,
   carry a Fastify JSON schema, and require authentication unless deliberately
   public.
5. Streak-affecting writes go through `arete_habit_completions` so the streak
   trigger fires.
6. `pnpm nx build`, `lint`, and `test` pass for every affected project.
