# Customer-Facing Domains

The six customer-facing domains — **Tara, Arete, Veritas, Nyx, Nisaba, Metis** —
are the experiences members actually open: contemplative ritual, humane habit
coaching, grounded news, sky-watching, scholarly reading, and structured
learning. Each is a self-contained vertical with its own Zod contracts, a typed
read adapter consumed by the shell/admin/assistant, a BFF route prefix, and a
web surface tree, yet all six share one registry, one persistence boundary, and
one set of platform substrates ([Sophia](./substrate-sophia.md),
[Iris](./substrate-iris.md), [Lilith](./substrate-lilith.md),
[Psyche](./substrate-psyche.md), [Isis](./substrate-isis.md)). This page is the
architectural reference for those six verticals; it sits below the
[High-Level Architecture](./high-level-architecture.md) and beside
[Product Surfaces](./product-surfaces.md), and it deliberately separates what is
genuinely shipped from what is spec-described, provider-gated, or planned.

## Shared Anatomy of a Domain

Every V1 domain follows the same physical layout, so the architecture is
predictable across all six:

| Layer              | Convention                                                                                                                  |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------- |
| **Contracts**      | `libs/contracts/src/<domain>/` — Zod schemas, cross-field `superRefine` guards, and spec tests                              |
| **Domain adapter** | `libs/oshun/domain-<domain>/src/` — typed read adapter and domain logic the shell/admin consume                             |
| **BFF route**      | `apps/oshun/bff/src/routes/<domain>.ts` plus read-adapters in `apps/oshun/bff/src/adapters/`                                |
| **Web surface**    | `apps/oshun/web/src/app/<domain>/` (consumer hub) plus a power-user `domains/<domain>/` namespace                           |
| **Persistence**    | `@oshun/persistence` for durable data; BFF member stores and client `localStorage['oshun.<domain>']` for the hydrated shell |
| **Registry**       | one entry in `libs/oshun/domain-registry/src/registry.ts` (the source of truth)                                             |

The contracts are the contract: they are not anemic CRUD types but
domain-specific schemas that encode the rules of the domain directly in
`superRefine` blocks (a completed ritual must have ≥ 80 % audio completion; a
`done` check-in must be exactly 100 % engagement; a high-confidence concept edge
must cite evidence). That is what keeps the adapters, BFF, and surfaces honest:
an invalid state cannot be constructed.

### Domain Registry

The single source of truth lives at
`libs/oshun/domain-registry/src/registry.ts`, keyed by `OSHUN_DOMAIN_IDS`. Each
entry declares the BFF base path, deep-link prefix, notification channel, and
the assistant context key the shell threads into Iris:

| Domain  | BFF base path                | Deep-link prefix  | Notification channel          | Assistant context key    |
| ------- | ---------------------------- | ----------------- | ----------------------------- | ------------------------ |
| Tara    | `/api/oshun/domains/tara`    | `oshun://tara`    | `oshun.tara.rituals`          | `taraRitualContext`      |
| Veritas | `/api/oshun/domains/veritas` | `oshun://veritas` | `oshun.veritas.claim-updates` | `veritasEvidenceContext` |
| Nyx     | `/api/oshun/domains/nyx`     | `oshun://nyx`     | `oshun.nyx.sky-events`        | `nyxObservationContext`  |
| Arete   | `/api/oshun/domains/arete`   | `oshun://arete`   | `oshun.arete.accountability`  | `areteCoachingContext`   |
| Nisaba  | `/api/oshun/domains/nisaba`  | `oshun://nisaba`  | `oshun.nisaba.study`          | `nisabaStudyContext`     |
| Metis   | `/api/oshun/domains/metis`   | `oshun://metis`   | `oshun.metis.learning`        | `metisLearningContext`   |

`OSHUN_SHELL_PRIMARY_DOMAIN = 'tara'` fixes the shell's home ordering, and
`libs/oshun/domain-registry/src/guards.ts` validates cross-domain references at
boundary entry. Each entry also carries an **`availability`** field that gates
whether the domain is exposed in `getAvailableDomains()`. This matters: Tara,
Arete, Veritas, Nyx, and Nisaba are live, but **Metis is registered with
`availability: 'planned'`** (`registry.ts:489`), so it is filtered out of the
launchable set even though its contracts and libraries are extensively built.
Publishing the companion ownership matrix at `docs/oshun/ownership-matrix.md` is
a tracked V1 deliverable under §1.1; until it lands, the registry above is the
source of truth for per-domain ownership.

---

## Tara — Rituals, Breathwork, and Contemplative Continuity

**Purpose:** rituals, practices, breathwork, meditation, teachers, and
contemplative continuity. Tara is the experiential and thematic center of V1 —
the registry's primary domain — and its contracts are among the richest in the
repo. `libs/contracts/src/tara/index.ts` alone is **2,688 lines** (re-exported
via `export * from './tara'` at `libs/contracts/src/index.ts:25`).

### Packages

- **Contracts** — `libs/contracts/src/tara/` (re-exported from
  `@oshun/contracts`).
- **Domain adapter** — `libs/oshun/domain-tara/src/` is a ~50-file library, not
  a thin wrapper: `adapter.ts` and `canonical-adapter.ts` (read adapters), the
  play/pause/scrub/mix/sleep-fade runtime in `audio-session.ts` (22 KB) and
  `audio-session-manager.ts` (11 KB), `ritual-scheduling.ts` (16 KB),
  `ritual-assembly.ts` (14 KB), `ritual-model.ts` (17 KB), `practice-models.ts`
  (24 KB), and `types.ts` (35 KB). Its `index.ts` also re-exports the
  cross-domain relationship modules `lilith-ritual-tone-review`,
  `lilith-mood-crisis-handoff`, `arete-relationship`, `nisaba-relationship`,
  `nyx-relationship`, and `metis-relationship`.
- **Two supporting library families** back Tara beyond the domain adapter: the
  `@tara/*` family (`@tara/content`, `@tara/config`, `@tara/api-client`,
  `@tara/monitoring`, `@tara/ui`, `@tara/features`, `@tara/analytics`,
  `@tara/database`) and the `@oshun/meditation-*` family
  (`@oshun/meditation-core`, `-breathing`, `-session`, `-player`, `-progress`,
  `-timer`, `-offline`, `-analytics`).
- **BFF** — routing at `apps/oshun/bff/src/routes/tara.ts` plus read adapters in
  `apps/oshun/bff/src/adapters/tara-read-adapters.ts`.
- **Surfaces** — the consumer hub is `apps/oshun/web/src/app/tara/page.tsx`, and
  the **immersive session page is `apps/oshun/web/src/app/tara/sit/[id]/`** (the
  `/tara/sit` route is the actual session surface — easy to omit from a hub-only
  list). The power-user namespace `apps/oshun/web/src/app/domains/tara/` ships
  `analytics`, `collections`, `courses/[id]`, `programs`, `search`, `sounds`,
  and `teachers/[id]`. On mobile, **there is no `tara/` screen directory** — the
  mobile app (`apps/oshun/mobile/src`) is organized by feature, and Tara appears
  as companion components such as `TaraNyxPerspectiveCompanionCard.tsx`,
  `TaraVeritasSophiaCompanionCard.tsx`, `TaraNisabaPassageCompanionCard.tsx`,
  and `TaraAreteNextStepCompanionCard.tsx` rather than a dedicated surface tree.

### The canonical taxonomies are shipped, validated datasets

The most important Tara fact the prior docs underplayed: the taxonomy axes are
not just types — they are **runtime reference datasets** with real values that
the schemas validate against.

| Dataset                  | Entries | What each entry carries                                                                                         |
| ------------------------ | ------- | --------------------------------------------------------------------------------------------------------------- |
| `TARA_MOOD_TAXONOMY`     | 12      | `distressLevel`, `recommendationSlate`, and a `crisisHandoff { required, reason }` gate                         |
| `TARA_THEME_TAXONOMY`    | 15      | `compatibleMoods`, `defaultModalities`                                                                          |
| `TARA_MODALITY_TAXONOMY` | 14      | `family`, `requiresAudio`, `sensoryLoad`, `accessibilityFallback`, `contraindicationNotes`, `breathworkCadence` |
| `TARA_LINEAGE_TAXONOMY`  | 8       | real teachers, scriptural citations, and `syncretism` gates                                                     |
| `TARA_CONTEXT_TAGS`      | —       | situational tags (e.g. waking, commuting, before-sleep)                                                         |
| `TARA_DURATION_BUCKETS`  | 5       | min/max minutes plus recommendation cadence (below)                                                             |

The **`MoodTaxonomySchema`** enum is exactly twelve values: `anxious`,
`scattered`, `restless`, `heavy`, `low`, `neutral`, `curious`, `joyful`,
`agitated`, `grieving`, `fearful`, `peaceful`. Each `TARA_MOOD_TAXONOMY` entry
binds a `distressLevel` and a `recommendationSlate` to that mood, and the
high-distress moods carry a real crisis hand-off — e.g. `agitated` is
`distressLevel: 'high'` with
`crisisHandoff: { required: true, reason: 'Agitated high-distress mood should route through crisis-aware safety review.' }`.
This is the seam through which Tara hands a struggling member to
[Lilith](./substrate-lilith.md) via the `lilith-mood-crisis-handoff` module —
the data model enforces that distress is never silently ignored.

The **`LineageTaxonomyIdSchema`** enumerates eight contemplative lineages —
`secular-breath-awareness`, `theravada-anapanasati`, `mahayana-bodhicitta`,
`vajrayana-tara-devotion`, `yogic-pranayama`, `bhakti-devotional-prayer`,
`advaita-self-inquiry`, `comparative-contemplative-study` — and the dataset
attaches **real teachers and scriptural references** to each: Buddhaghosa
(Visuddhimagga) and Gautama Buddha for Theravada with citations _MN 118_
(Anapanasati Sutta) and _MN 10_ (Satipatthana Sutta); Shantideva
(Bodhicaryavatara) for Mahayana; Padmasambhava for Vajrayana; Patanjali for
yogic; Mirabai for bhakti. Each lineage also carries a `syncretism` block
(`allowsCrossLineage`, `comparativePersonaRequired`, `disclosureLabel`), so a
practice that crosses lineages is gated on a comparative persona and a
disclosure label — the schema makes uncritical syncretism a validation error.

### Breathwork cadences are typed, not prose

The **14-value `ModalityTaxonomySchema`** includes four breathwork modalities
(`breathwork-box`, `breathwork-4-7-8`, `breathwork-coherent`,
`breathwork-alternate-nostril`) and three sound modalities
(`sound-singing-bowl`, `sound-drone`, `sound-mantra`). Each breathwork modality
in `TARA_MODALITY_TAXONOMY` carries a concrete `BreathworkCadence`:

| Modality              | inhale | hold | exhale | hold | Cycle                                                       |
| --------------------- | ------ | ---- | ------ | ---- | ----------------------------------------------------------- |
| `breathwork-box`      | 4 s    | 4 s  | 4 s    | 4 s  | "Four equal phases: inhale, hold, exhale, hold."            |
| `breathwork-4-7-8`    | 4 s    | 7 s  | 8 s    | 0 s  | "Inhale four, hold seven, exhale eight with no final hold." |
| `breathwork-coherent` | 5 s    | 0 s  | 5 s    | 0 s  | "Even five-second inhale and five-second exhale."           |

`BreathworkCadenceSchema` bounds each phase to 0–60 s, so the pacing is part of
the contract. Every breathwork modality also declares `contraindicationNotes`
(e.g. "Avoid long holds for dizziness, panic spikes, or respiratory strain") and
an `accessibilityFallback` (haptic pacing and count transcript) — pacing is
delivered to hearing-impaired members via vibration, which
`TaraContentAccessibility.vibrationPacingAvailable` flags per-practice.

### Duration buckets carry a recommendation-cadence policy

`TARA_DURATION_BUCKETS` defines five buckets with a built-in anti-spam policy —
`minimumHoursBetweenRecommendations` — so micro practices can be re-offered
quickly while a retreat is not pushed again for a week:

| Bucket     | Minutes      | Min hours between recommendations |
| ---------- | ------------ | --------------------------------- |
| `micro`    | 0–2          | 0.5                               |
| `short`    | 3–10         | 8                                 |
| `standard` | 11–25        | 12                                |
| `long`     | 26–60        | 72                                |
| `retreat`  | 61+ (no max) | 168                               |

### The session is a real state machine

`SessionStateSchema` is an 8-state enum — `not-started`, `started`, `paused`,
`drifted`, `resumed`, `abandoned`, `completed`, `partially-completed` — and the
contract ships a real transition table, `SESSION_STATE_TRANSITIONS`, that the
`RitualSession` schema enforces through `validateSessionTimeline`:

```ts
const SESSION_STATE_TRANSITIONS = {
  'not-started': ['started'],
  started: [
    'paused',
    'drifted',
    'abandoned',
    'completed',
    'partially-completed',
  ],
  paused: ['drifted', 'resumed', 'abandoned'],
  drifted: ['resumed', 'abandoned'],
  resumed: [
    'paused',
    'drifted',
    'abandoned',
    'completed',
    'partially-completed',
  ],
  abandoned: ['resumed'],
  completed: [],
  'partially-completed': [],
};
```

Each `RitualSessionEvent` (type `start`/`pause`/`drift`/`abandon`/`edit`/…)
carries `priorState` and `newState`, and `superRefine` enforces both that the
event sequence is contiguous (each event's `priorState` equals the prior event's
`newState`) and that each event type lands in the right state — **a `drift`
event must enter the `drifted` state** (`index.ts:2494`). The `RitualSession`
also enforces domain invariants: a `completed`/`partially-completed` session
requires ≥ 80 % `audioCompletionPercent` and a `completedAt`; a
`partially-completed` session must stay below 100 %; an `abandoned` session
requires `abandonedAt`.

> **Drift accuracy note.** The companion spec (features.md) describes `drifted`
> as "idle beyond the per-modality `drift_idle_seconds` threshold." In code
> there is **no `drift_idle_seconds` (or `driftIdleSeconds`, `idleThreshold`)
> constant anywhere** in `libs/oshun/domain-tara/src` or
> `libs/contracts/src/tara`. Drift is modeled as a `RitualSessionEvent` of type
> `'drift'` that must transition into the `drifted` state, plus a nullable
> `driftDetectedAt` timestamp on the session. There is no numeric per-modality
> idle threshold — drift is an event, not a tuned scalar.

The domain adapter's `types.ts` does ship two real lifecycle constants:
`TARA_SESSION_CHECKPOINT_THRESHOLD_PERCENT = 5` (checkpoints once 5 % of audio
has played) and `TARA_SESSION_RECENT_COMPLETION_WINDOW_HOURS = 12` (what counts
as a recent completion for resume/recommendation).

### Continuation is richer than "next up"

`ContinuationState` is the spine of Tara's "pick up where you left off" promise.
Its `programArc` carries `completedSessions`/`totalSessions`/
`currentSessionIndex` with invariants (`completedSessions` cannot exceed
`totalSessions`; `currentSessionIndex` cannot exceed `totalSessions`). It tracks
per-theme `themeContinuity` depth (`introductory`/`developing`/`deepening`), and
its `nextRecommendation` is **prerequisite-blocked**: if
`prerequisitesSatisfied` is false, the `blockedPrerequisiteIds` must reference
actually unsatisfied prerequisites, and a satisfied recommendation cannot carry
blocked ids. A `governance { memoryScope, consentRecordId }` block ties the
continuation to a consent record, so resume is consent-aware by construction.

### Playback rate is a real normalization function

`libs/contracts/src/tara/playback-rate.ts` implements the documented
"0.85×–1.25× voice speed" as an actual contract, not a slider hint. The default
`QUALITY_PRESERVING_PLAYBACK_RATE_POLICY` is
`{ minRate: 0.85, maxRate: 1.25, step: 0.05, decimalPlaces: 2, preservePitch: true }`,
and `normalizePlaybackRate()` snaps to the step grid, clamps to the range, and
throws a `PlaybackRateRangeError` for out-of-range or non-finite input. The
narrow range exists because, as the file documents, "breath cues, room tone, and
vocal formants degrade quickly outside this narrow range."

### TeacherProfile

`TeacherProfileSchema` carries `id` (slug), `displayName`, `biography`,
`lineage`, `roles` (one or more of `teacher`/`breath-guide`/`narrator`/
`scholar`/`reviewer`), `credentialSummary`, `localeCoverage`, a
`rights: TaraContentRights` block (license/consent/attribution), and an
`accessibility { voiceDescription, transcriptVoiceName }` block.

### Cross-domain hooks

Tara's cross-domain hooks span Nisaba passage companions, Arete next steps, Nyx
perspective prompts, Veritas/Sophia explanatory notes, assistant follow-ups,
Lilith contemplative tone gating, and Living Scenes Contemplative-Arc cards. The
Living Scenes contracts exist at
`libs/contracts/src/living-scene/{index.ts,score.ts, technique.ts}` (see
[Living Scenes](./living-scenes.md)); companion components are in place, but the
full immersive Contemplative-Arc runtime that paces card transitions to breath
cycles is partially evidenced rather than fully confirmed. Per the V1
completeness audit (`v1-completeness-audit-2026-06-22.md`), the
`tara-daily-ritual`, `first-tara-sit`, and `tara-to-nisaba-handoff` journeys are
rated **deep** coverage — the experiential spine is genuinely shipped — while
several cross-domain bridges remain unit-only. Cross-device resume and
watermarked downloads with attribution are spec-described and only partially
evidenced.

---

## Arete — Goals, Habits, and Humane Streak Coaching

**Purpose:** goals, habits, routines, journaling, weekly review, and humane
streak/recovery coaching. Arete's design thesis is anti-shame: missing a day is
modeled, named, and recoverable — never a silent zero. The contracts
(`libs/contracts/src/arete/index.ts`, **1,207 lines**, re-exported via
`export * from './arete'` at `libs/contracts/src/index.ts:22`) encode that
thesis directly.

### Packages

- **Contracts** — `libs/contracts/src/arete/`.
- **Domain adapter** — `libs/oshun/domain-arete/src/`. The two load-bearing
  modules are `streak-recovery.ts` (21 KB), which exports the real recovery
  algorithms `evaluateAreteHabitRecovery` (`:111`),
  `applyAreteHabitRecoveryCompletion` (`:317`), `countMissedHabitWindows`
  (`:371`), and `ARETE_HUMANE_STREAK_POLICY` (`:86`); and
  `friction-taxonomy.ts`, which exports
  `ARETE_FRICTION_TAXONOMY_VERSION = '1.0.0'`, `ARETE_FRICTION_DESCRIPTORS`,
  `ARETE_INTERVENTION_DESCRIPTORS`, `buildAreteInterventionRecommendations`, and
  `summarizeAreteFrictionLogs`.
- **Library family** — `@arete/*` (12 packages: `@arete/affirmations`,
  `@arete/ai-coach`, `@arete/balance`, `@arete/gamification`, `@arete/goals`,
  `@arete/habits`, `@arete/journal`, `@arete/seven-habits`, `@arete/time`,
  `@arete/vision`, `@arete/core`, `@arete/database`, plus `@arete/api-client`).
- **Service tier** — `apps/arete/{api,mobile,web}` exists, but the member-scoped
  persistence the audit exercises actually lives in BFF stores
  (`apps/oshun/bff/src/arete/arete-review-store.ts`, `arete-offering-store.ts`,
  `arete-coach-decision-store.ts`) and client-side `useAreteStore` /
  `localStorage['oshun.arete']` — not solely in `apps/arete`.
- **BFF** — `apps/oshun/bff/src/routes/arete.ts` exposes `/v1/arete/room`,
  `/v1/arete/habits` (and `/:habitId/check-in`), `/v1/arete/offerings/keep`,
  `/v1/arete/offerings/sent`, `/v1/arete/review/close`, and
  `/v1/arete/review/closed`.
- **Surfaces** — `apps/oshun/web/src/app/arete/` ships `coaching`, `goal`,
  `habits`, `offering` (singular), `offerings` (plural — a distinct directory),
  `patterns`, `plan`, `recovery`, `review`, `streak`, and `weekly`; the
  power-user namespace `apps/oshun/web/src/app/domains/arete/` adds
  `affirmations`, `balance`, `coach`, `gamification`, `goals`, `habits`,
  `journal`, `plan-review`, `progress`, `seven-habits`, `time`, and `vision`.

### Check-in status drives streak treatment

`CheckInStatusSchema` is exactly
`['done', 'partial', 'skip', 'decline', 'miss']`, and each status maps to a
streak treatment through the real `CHECK_IN_STREAK_TREATMENT` table (plus the
helpers `getCheckInStreakTreatment` and `isEngagedCheckInStatus`):

| Status    | Streak treatment | `superRefine` invariant                     |
| --------- | ---------------- | ------------------------------------------- |
| `done`    | `engaged`        | requires `engagementPercent === 100`        |
| `partial` | `engaged`        | requires `engagementPercent > 0 && < 100`   |
| `skip`    | `grace`          | requires `0 %` and a visible `statusReason` |
| `decline` | `grace`          | requires `0 %` and a visible `statusReason` |
| `miss`    | `no-count`       | requires `0 %`                              |

That `done`/`partial`/`skip`/`decline`/`miss` list in the architecture index is
confirmed accurate against the code. The point of the table is that **`skip` and
`decline` preserve the streak via grace** and require an honest reason — a
member who consciously skips is never punished, but the system records why.

### Grace is configurable, not a fixed 24h/72h

The companion spec describes "24 h grace on daily habits; 72 h grace on weekly
cadence." Those are **spec defaults, not code constants.** In the contract,
grace is modeled per-habit: `DeclaredCadence.graceWindowHours` is an integer
0–168, and `HumaneStreakPolicy` carries `skipPreservesStreak`,
`declinePreservesStreak`, `missGraceCadences` (0–30),
`recoveryPromptAfterMisses`, and a `visualLanguage` of `no-shame`/`neutral`. The
24/72 figures should be read as defaults a habit may carry, not hardcoded gates.

### The streak, missed-day, and recovery model

The humane-recovery story rests on three contracts the prior docs omitted:

- **`StreakVisualState`** = `steady` | `grace` | `drift` | `recovery` | `paused`
  — the visible state a streak presents.
- **`MissedDay`** (with `MissedDayDisposition` = `within-grace` | `logged-miss`
  | `recovered` | `excused`) — `superRefine` enforces that a `logged-miss`
  carries a visible reason and `no-count` treatment.
- **`RecoveryRecord`** and **`Plan`** (with `PlanCommitment`) — recovery plans
  require a `recoveryRecordId`, so a recovery is always backed by a real record.

Other real contracts the index list omits: `AreteMetric`, `MoodSnapshot`,
`DeclaredCadence`, `HumaneStreakPolicy`, and the `CHECK_IN_STREAK_TREATMENT`
constant itself.

### Friction signals and interventions are full taxonomies

`FrictionSignalKindSchema` has **11 values** — `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`. `InterventionKindSchema` has **9 values** —
`notification-retiming`, `plan-rescope`, `alternative-habit`,
`accountability-check-in`, `breathwork-insert`, `tara-ritual-surfacing`,
`weekly-review-prompt`, `routine-substitution`, `goal-criteria-clarification`.
The `tara-ritual-surfacing` intervention is the explicit bridge from a habit
friction into a Tara practice — coaching can prescribe a contemplative reset.

### CoachingSummary and WeeklyReview are structured, not narrative

`CoachingSummary` carries structured `observedPatterns` with a `kind` enum
(`time-of-day`, `day-of-week`, `mood-correlation`, `habit-interaction`,
`seasonality`, `location`, `post-event`), a `supportBand`
(`weak`/`moderate`/`strong`), and a `governance.memoryScope` — the coach's
observations are typed signals, not free text. `WeeklyReview` ships the exact
**celebrate / notice / choose / invite** quadrants as a real schema
(`WeeklyReviewSectionSchema`), and the review carries `planAdjustments`,
`nextPracticeRecommendations`, and `crossDomainRefs`.

### Living Offerings

Arete hosts **Living Offerings** — 4–8 min Living Scenes tuned to a stated
intention, watermarked, kept in a personal gallery with opt-in share. Per the
2026-06-22 audit, `arete-create-habit`, `arete-living-offering-create`,
`weekly-review-arete`, and `arete-streak-recovery` are all rated **partial**.
The wizard create flow POSTs the real BFF (commit `65e3f609e0`, "test(arete):
persist habit wizard creations"), but cross-device sync is uncovered, the
living-offering intention capture is still hardcoded JSX rather than a real
textarea, and the session→streak write plus grace-window timing remain in
progress. The data model and core logic are real; some end-to-end UI/persistence
and the full Living-Offerings render pipeline are not yet wired.

---

## Veritas — Grounded Stories, Claims, and Retraction Cascades

**Purpose:** grounded stories, claims, sources, evidence, counterclaims, topic
hubs, timelines, explainers, and trust/confidence display. Every Veritas claim
is grounded through [Sophia](./substrate-sophia.md), and retractions cascade
into both Veritas surfaces and Sophia evidence packs.

### Packages and the two-tier adapter

The canonical domain is `@oshun/domain-veritas` at `libs/oshun/domain-veritas`,
with the cascade worker at `@oshun/veritas-cascade-worker`. (Note:
`libs/veritas` is a **separate** collection of ~66 sub-libs — `agents-*`,
`fact-checking`, `bias-detection`, `claims`, `knowledge-graph`,
`headline-service` — not the canonical domain.) `libs/contracts/veritas/`
contains **only a `.gitkeep`** — it is not a wired package; Veritas types are
imported as `@oshun/contracts/veritas`, which the path map resolves to
`libs/contracts/src/veritas`.

Veritas ships **two adapters**, which the prior docs collapsed into one:

1. A **presentational article-feed adapter** in
   `libs/oshun/domain-veritas/src/types.ts` — `VeritasApiAdapter` with
   `getTrendingArticles`, `getTopClaims`, `getClaimDetail`,
   `saveArticle`/`unsaveArticle`, `followTopic`/`unfollowTopic`, and
   `getHealth`. Its presentational types include `VeritasVerdict` (8 values:
   `verified`, `likely_true`, `disputed`, `misleading`, `mostly_false`, `false`,
   `unverifiable`, `unverified`), `VeritasCredibilityTier`
   (`high`/`medium`/`low`/`unknown`), and `VeritasClaimType` (7 values).
2. The **canonical editorial model** (state machine, source quality,
   counterclaim balance, retraction cascade) described below.

The read adapter enforces a real RBAC surface
(`libs/oshun/domain-veritas/src/adapter.ts:81`):
`VERITAS_ADAPTER_READ_CAPABILITIES` has 10 entries — `contract_descriptor`,
`metadata`, `availability`, `home_cards`, `continue_items`, `search`, `launch`,
`saved_articles`, `trending_topics`, `bridge_contexts` — gated per role (`shell`
| `admin` | `assistant`) via `VERITAS_ADAPTER_ROLE_CAPABILITIES`.

**Surfaces** — `apps/oshun/web/src/app/veritas/` ships `claim`, `counterclaims`,
`evidence`, `mobile`, `provenance`, `retraction`, `source`, `story`, and
`topic`; the power-user namespace `apps/oshun/web/src/app/domains/veritas/` adds
`articles`, `bias`, `claims/[claimId]`, `fact-check`, `headlines`,
`knowledge-graph`, `newsletter`, `nlp`, `research`, `story/[id]`, and
`topics/[topicId]`. The `/veritas/evidence` route is real and easy to miss from
a hub-only list.

### Source quality is a deterministic composite

`Source` carries a `SourceKind` that is **a superset of 15 kinds** (the prior
list of 8 was a subset): `peer-review`, `primary`, `secondary`, `press-release`,
`opinion`, `social`, `government`, `ngo`, plus `wire`, `dataset`,
`court-record`, `transcript`, `image`, `video`, `audio`. Source quality is a
**geometric weighted mean** over per-domain factor tables
(`source-quality/composite.ts`), with `FACTOR_FLOOR = 0.05` so no single factor
can zero out the score, and a `bandFromComposite` step:

| Composite | Band    |
| --------- | ------- |
| ≥ 82      | `high`  |
| ≥ 60      | `mixed` |
| ≥ 35      | `low`   |
| else      | `low`   |

Three hard overrides sit on top of the composite: `unattributed-capped-low`,
`recent-retraction-drops-one-band` (within ≤ 12 months), and
`retracted-forces-contested`. `SourceQualityBandSchema` is
`['high', 'mixed', 'low', 'contested']`; `ClaimConfidenceBandSchema` runs
`well-supported` → `supported` → `emerging` → `contested` → `unsupported` →
`retracted`.

### The editorial state machine (kebab-case, in code)

The implemented `VeritasEditorialState` union
(`libs/oshun/domain-veritas/src/editorial/state-machine.ts:1-13`) is **all
kebab-case**, not snake_case: `draft`, `in-review`, `verifying-sources`,
`awaiting-attestation`, `contradicts-existing`, `counterclaim-pending`,
`approved`, `scheduled`, `published`, `archived`, `corrected`, `retracted`.
`TRANSITION_RULES` pairs each `(from, event)` with gate functions —
`requireRationale`, `requireReviewer`, `requireOccurredAt`, `requireSources`,
`requireSourcesVerified`, `requireAttestations`, `requireContradictions`,
`requireOpenContradictionsResolved`, `requireCounterclaims`,
`requireCascadeScope`.

The real path differs from the illustrative mermaid in the hub:
**`verifying-sources` --sources-verified--> `awaiting-attestation`**, then
**`awaiting-attestation` --attestation-collected--> `approved`**, then
**`approved` --publish--> `published`** (or via `scheduled`). There is **no
direct `awaiting-attestation` → `published` edge** and no edge feeding back into
`in-review` from the verification states — those branches in the diagram are
spec illustration. Note also that the contracts carry a **third, shorter state
vocabulary** for the presentational story: `StoryEditorialStateSchema`
(`libs/contracts/src/veritas/index.ts:79`) is
`['draft', 'review', 'published', 'updated', 'corrected', 'retracted', 'archived']`
— no verification states at all. Three vocabularies describe the same lifecycle
at different layers; the kebab-case state machine is the authoritative one for
editorial flow.

### Counterclaim balance, topic hubs, retraction UX

- **Counterclaim surfacing** (`counterclaim/surface.ts`):
  `DEFAULT_SURFACING_POLICY` has `tenantBandFloor: 'mixed'`,
  `consensusThreshold: 0.85`, `recencyWindowDays: 365`, and six
  `publicSafetyTopics` (`public-health`, `product-safety`, `biosecurity`,
  `aviation-safety`, `nuclear-safety`, `natural-disaster-response`). The
  decision is a 4-way union: `co-equal` | `minority-view-expand` |
  `mandatory-surface` | `suppress`.
- **Topic hubs** (`topic-hub/composer.ts`): `composeTopicHub` arranges six
  sections —
  `TOPIC_HUB_SECTIONS = ['latest', 'key-facts', 'under-dispute', 'corrections', 'background', 'timeline']`
  — using `DEFAULT_RANKING_WEIGHTS`.
- **Retraction UX** (`retraction-ux/banner.ts`): `RetractionSurfaceKind` is
  `'story-page'` | `'saved-claim'` | `'notebook'` | `'living-offering'`;
  `RetractionSeverity` is `'silent'` | `'banner-only'` |
  `'notification-included'`; `severityFromBandChange()` derives severity from
  the band delta; and per-surface builders `buildStoryPageBanner`,
  `buildSavedClaimBanner`, and `buildNotebookNotice` produce the banners.
- **Contradiction probe** (`contradiction/probe.ts`): `DEFAULT_PROBE_OPTIONS`
  has `triageThreshold: 0.6`; `ContradictionTrigger` is `new-claim-published` |
  `source-update` | `attestor-disagreement` | `operator-flag` | `cadence-sweep`
  | `on-refresh`; it consumes a `SophiaEvidencePack` from
  `@oshun/evidence-sophia`.

### The retraction cascade

The cascade is the customer-facing payoff: when a story is retracted, the
`RetractionCascade` (`contracts/src/veritas/index.ts:549`) tracks
`affectedStoryIds`, `affectedClaimIds`, `affectedExplainerIds`,
`affectedEvidencePackIds`, plus **both `downstreamMetisPackageIds` and
`downstreamMetisLessonIds`** — Metis fan-out is tracked at two granularities,
not just "lessons." Its `reGroundingJobs` carry an action enum
(`RetractionCascadeJobActionSchema = ['re-ground', 'metis-revalidate']`), and a
`superRefine` forces Metis-lesson jobs to `'metis-revalidate'`.

Execution emits a real event:
`VERITAS_RETRACTION_CASCADE_DISPATCHED_EVENT = 'veritas.retraction.cascade.dispatched'`
(`retraction-cascade-execution.ts:37`). The `@oshun/veritas-cascade-worker`
subscribes to the `IEventBus` from `@oshun/event-bus` via
`subscribeRetractionCascadeWorker` and re-grounds downstream artifacts using
persistence-backed re-grounders — `createPersistenceBackedSophiaReGrounder`
(keyed by `SOPHIA_GROUNDED_ANSWER_STORE_KEY` /
`SOPHIA_REGROUNDING_VERDICT_STORE_KEY`) and
`createPersistenceBackedMetisRevalidator` (keyed by
`METIS_LESSON_SOURCES_STORE_KEY` / `METIS_REVALIDATION_VERDICT_STORE_KEY`).

> **Honest state.** The canonical contracts, state machine, source-quality
> composite, counterclaim balance, topic-hub composer, retraction-UX builders,
> contradiction probe, and cascade executor/worker are all real and enforced.
> The cascade's end-to-end customer journey (fan-out across notebook and
> Living-Offering surfaces, per-user gating, the live "downstream Metis lessons
> re-validated" wiring) is rated **partial** in e2e — the ports exist but live
> wiring sits at the deployable boundary. Per-tenant composite tightening,
> weekly drift re-computation, reader-notification quiet hours, and localized
> hub variants are spec-only.

---

## Nyx — Sky Events, Observation Windows, and Awe

**Purpose:** sky events, observation windows, awe-and-perspective, and calendar
integration. Nyx's astronomy core is the most impressive genuinely real asset of
the six domains, validated against known Meeus values.

### Packages

- **Contracts** — `libs/contracts/src/nyx/` (re-exported via
  `export * from './nyx'`).
- **Domain adapter** — `libs/oshun/domain-nyx/src/`, anchored by the **ephemeris
  engine** `ephemeris.ts` (19.7 KB) and a `depth/` directory of 41 files (20
  depth modules, tests, and a depth-page).
- **Library tree** — `libs/nyx/*` (~30 subdirs incl. `catalogs/`,
  `constellations/`, `coordinates/`, `orbital/`, `positional/`, `realtime/`,
  `renderer/`, `sky-clock/`, `time-travel/`, `client-python/`, `ephemeris/`,
  `mythology/`). The `@nyx` ephemeris is also consumed by V9.
- **BFF** — `apps/oshun/bff/src/routes/nyx.ts` exposes the adapter endpoints
  under
  `/v1/nyx/adapter/{capabilities,availability,home-cards,continue-items, search,launch,nightly-highlights,object-search,saved-objects,event-reminders, observation-logs,bridge-moments}`,
  plus `nyx-3d-route.ts`, `nyx-reminder-bridge.ts`, and `nyx-member-stores.ts`.
- **Surfaces** — the consumer hub `apps/oshun/web/src/app/nyx/` ships the
  `NyxRoom` (`page.tsx`) **and first-class consumer routes `events/`,
  `observation/`, `sky-almanac/`, and `tonight/`** (these are not just power
  tools). The power-user namespace `apps/oshun/web/src/app/domains/nyx/` adds
  `analysis`, `catalogs`, `coordinates`, `events/[eventId]`, `learning`, `moon`,
  `neo`, `observation-log`, `observation-log-deep`, `renderer`, `satellites`,
  `sky-conditions`, `solar`, `solar-system`, `sonification`, `star-chart`,
  `telescope`, `time-travel`, and `widgets`. On mobile, there is **no `nyx/`
  screen directory** — Nyx appears only as cross-domain companion components
  (e.g. `TaraNyxPerspectiveCompanionCard.tsx`).

### The ephemeris engine is real Meeus astronomy

`ephemeris.ts` exports `toJulianDay`, `julianCenturies`, `sunPosition`,
`moonPosition`, `moonIllumination`, `computeNightSky`,
`greenwichMeanSiderealTime`, `localSiderealTime`, `equatorialToHorizontal`,
`riseTransitSet` (using a `STANDARD_ALTITUDE` constant), and `computeTonight`.
This is not a stub by any diagnostic — `ephemeris.test.ts` asserts known-correct
Meeus values:

| Assertion                                        | Expected        |
| ------------------------------------------------ | --------------- |
| `toJulianDay(2000-01-01T12:00Z)` (J2000.0 epoch) | `2451545.0`     |
| `sun.eclipticLongitude`                          | ≈ `199.909°`    |
| `sun.declination`                                | ≈ `-7.78507°`   |
| `moon.eclipticLongitude`                         | ≈ `133.1627°`   |
| `moon.distance`                                  | ≈ `368409.7 km` |
| `moonIllumination.illuminatedFraction`           | ≈ `0.6786`      |

The 20 `depth/` modules — `lunar-phase-calendar`, `twilight-schedule`,
`solar-season-markers`, `object-transit`, `moon-phase-detail`,
`equation-of-time`, `zodiac-position`, `moon-distance`, `sun-distance`,
`lunar-nodes`, `planetary-hours`, `daylight-extremes`, `chart-angles`,
`solar-terms`, `astronomical-night`, `horizon-point`, `solar-horizon-points`,
`lunar-horizon-points`, `solar-noon`, `twilight-phase-now` — power the
`/domains/nyx/*` deep tools off this engine.

### Event taxonomy and per-family detail schemas

`SkyEventFamilySchema` has **11 families** (`meteor-shower`, `eclipse`,
`conjunction`, `occultation`, `transit`, `comet`, `aurora`, `satellite-pass`,
`supermoon`, `seasonal-marker`, `deep-sky-peak`); `SkyEventTypeSchema`
enumerates **27 types** (meteor-shower-peak; solar/lunar eclipses
partial/total/annular/penumbral; planet-planet/planet-moon/planet-star
conjunctions; mercury/venus/exoplanet transits; aurora-forecast; iss-pass;
supermoon; march/september equinoxes; june/december solstices; cross-quarter;
deep-sky-peak). Each family has a typed detail schema, e.g.:

- `MeteorShowerEventDetails`: `iauCode` (regex `/^[A-Z0-9]{3}$/`),
  `peakZenithalHourlyRate` (int 1–1000), `radiant`.
- `AuroraEventDetails`: `hemispheres` (`north`/`south`), `kpIndexMin`/`Max`
  (0–9), `geomagneticStormLevel` (`g1`–`g5`), `validForecastHours` ≤ 168.
- `EclipseEventDetails`: `sarosSeries`, `gamma`, contact times.
- `SatellitePassEventDetails`: `noradId`. `SupermoonEventDetails`:
  `perigeeDistanceKm`.

A `predictionSourceFamilySupport` mapping (with `superRefine` enforcement) ties
which source kinds can back which event families — an event must carry a
supporting source.

### Prediction sources and calendar providers

`PredictionSourceKindSchema` has **11 kinds**, five more than the prior docs
listed: `nasa-jpl-horizons`, `nasa-jpl-small-body-db`, `imo-meteor-calendar`,
`iers-bulletin-a`, `noaa-swpc-geomagnetic-forecast`, `weather-provider`,
`bortle-light-pollution-atlas`, `tle-provider`, `minor-planet-center`,
`usno-astronomical-applications`, `gaia-catalog`. `CalendarProviderSchema` is
`['google', 'apple', 'outlook', 'ics-file']` — the `ics-file` provider (with its
own `externalCalendarId`-null special case) is real. `ReminderCadenceSchema` is
`['peak-only', 'evening-of', 'week-before', 'none']`.

### Observation quality and the observe-loop

`ObservationQualityBandSchema` is
`['excellent', 'good', 'fair', 'poor', 'not-visible']`, and `excellent` is gated
by `superRefine`: `bortleClass ≤ 4`, `cloudCover ≤ 25 %`, twilight in
`astronomical`/`night`. Beyond saving/following/reminding, Nyx ships a real
**`LoggedObservation`** contract (`index.ts:784`) — the ground-truth loop where
a member reports what they actually saw: `site`, `skyCondition`
(bortle/seeing/transparency/weather/cloud/moon %), `equipment`, `qualityRating`,
`attachments`, `sharedScope` (`private`/`household`/`public`), and
`sourceClient`. The key contracts `SkyEvent`, `ObservationWindow`,
`ObservationQualityBand`, `PredictionSourceRef`, and `CalendarSyncEntry` all
exist and are confirmed accurate.

> **Honest state.** The astronomy core, the full contract model, and local
> persistence (`useNyxStore` / `localStorage['oshun.nyx']`) are real. Per the
> 2026-06-22 audit, `nyx-event-calendar-sync-reminder` is **partial** — two-way
> provider sync is mocked at the boundary and reminder dispatch/delivery remain.
> `nyx-tonight-observation` is now **deep** for shipped local/dev infra: the
> browser journey proves local-first observation save, `/v1/nyx/observations`
> POST/GET read-back after local storage clear, Home Nyx support-card read-back,
> current-week KPI increment, and the observation-log versus telescope-control
> equipment boundary. Live external data-source ingestion (actual NASA/IMO/NOAA
> fetches) and real calendar-provider OAuth sync are seam-mocked / fail-loud,
> which is the honest state, not a fabrication.

---

## Nisaba — Scholarly Passages, Editions, and Polyglot Philology

**Purpose:** scholarly passages, sources, manuscripts, editions, translations,
lexicon and morphology, concept graph, notebooks, and study plans. Nisaba pairs
a fully specified canonical contract layer with the richest real philology code
in the repo — but its hydrated shell is the least connected of the six.

### Packages

- **Contracts** — `libs/contracts/src/nisaba/` (re-exported as the
  `NisabaContracts` namespace). Fully implemented Zod schemas with cross-field
  `superRefine`.
- **Domain adapter** — `@oshun/domain-nisaba` at `libs/oshun/domain-nisaba` is a
  **thin shell adapter**; the deep substrate is the contracts plus the
  `libs/nisaba/*` collection.
- **`libs/nisaba/*`** is ~23 sub-packages: `@nisaba/annotations`,
  `@nisaba/assistant`, `@nisaba/canon`, `@nisaba/comparative`,
  `@nisaba/corpora`, `@nisaba/criticism`, `@nisaba/editions`,
  `@nisaba/geotemporal`, `@nisaba/languages`, `@nisaba/paleography`,
  `@nisaba/philology`, `@nisaba/schemas`, `@nisaba/standards`,
  `@nisaba/study-plans`, `@nisaba/translations`, `@nisaba/workspace`,
  `@nisaba/database`, `@nisaba/cross-domain`, `@nisaba/mobile`, `@nisaba/core`,
  `@nisaba/client`, `@nisaba/api-client`.
- **Surfaces** — `apps/oshun/web/src/app/nisaba/` ships `compare`, `daily`,
  `graph`, `lexicon`, `manuscript`, `notebook` (singular), `notebooks` (plural —
  a distinct directory), `plan`, and `scholar`. There is **no literal
  `apps/oshun/web/src/app/domains/nisaba/` directory** (unlike Veritas): the
  power-user surface is served via the dynamic catch-all route
  `apps/oshun/web/src/app/domains/[domainId]/page.tsx` mounting
  `components/domains/NisabaSurface.tsx`.

### The canonical contracts

`NisabaScriptSchema` covers `latin`, `greek`, `hebrew`, `arabic`, `devanagari`,
`pali-sinhala`, `coptic`, `cuneiform`, `tibetan`, `chinese`, `japanese`,
`syriac`, `ethiopic`, `other` (and `TextDirectionSchema` includes
`boustrophedon`). The core schemas encode scholarly correctness in
`superRefine`:

| Contract           | Key fields / invariant                                                                                                                                                                                |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Passage`          | `CanonicalReference` (scheme `cts`/`osis`/`sefaria`/`library-of-congress`/`custom`), segments, sourceLineage, concept/lexicon/morphology bindings                                                     |
| `Manuscript`       | `siglum`, `repository.shelfmark`, `dateRange`, `material` (`papyrus`/`parchment`/`paper`/`stone`/`clay`/`metal`/`digital`), `digitization.status` requires `imageManifestUrl` when `iiif`/`digitized` |
| `Edition`          | `editionType` (`critical`/`diplomatic`/`reader`/`digital`/`facsimile`); `apparatus: ApparatusEntry[]` whose `witnessManuscriptIds` are validated to be declared in `edition.manuscriptIds`            |
| `Translation`      | `translationMode` (`literal`/`formal-equivalence`/`dynamic`/`commentarial`/`adaptive`); `superRefine` enforces `sourceLanguage !== targetLanguage`                                                    |
| `LexiconEntry`     | `lemma`, `senses`, `glosses`, `roots`                                                                                                                                                                 |
| `MorphologyEntry`  | `PartOfSpeech` (12 values), `MorphologyFeature` (14 values: case/number/gender/person/tense/aspect/mood/voice/state/degree/stem/root/prefix/suffix), parsing method                                   |
| `ConceptGraphNode` | `kind` (10): `term`/`deity`/`place`/`person`/`textual-theme`/`ritual-practice`/`cosmology`/`historical-event`/`school`/`manuscript-family`                                                            |
| `ConceptGraphEdge` | `relation` (9, below); high-confidence edges require `evidencePassageIds` or `citationIds`                                                                                                            |
| `StudyPlan`        | `StudyPlanStep.kind` (9): `read-passage`/`compare-translation`/`view-manuscript`/`lexicon-review`/`morphology-review`/`concept-map`/`annotation`/`reflection`/`assessment`; steps strictly ordered    |
| `ScholarProfile`   | `reviewAuthority { canApproveEditions, canApproveTranslations, canVerifyCitations, maxReviewRisk }`; `scholarCanVerifyCitations()` requires ≥ 1 verified credential                                   |

`CitationStyleSchema` is `['chicago', 'mla', 'apa', 'sbl', 'cts', 'custom']` and
`RightsStatusSchema` is
`['public-domain', 'open-license', 'licensed', 'restricted', 'unknown']`. The
schema also defines nested types the index list elides — `PassageSegment`,
`ApparatusEntry`, `LexiconSense`, `NotebookItem`, `StudyPlanStep`,
`TextRangeSelector`, `CanonicalReference` — plus helpers `isPublishedPassage`,
`conceptEdgeRequiresEvidence`, and `scholarCanVerifyCitations`.

> **Edge-label correction.** Earlier revisions of the companion spec described
> Nisaba concept edges as `refers-to`/`derives-from`/`comparative-to`/
> `lineage-of` (since corrected there too). **None of those four match the
> implemented enum.** The real `ConceptGraphEdgeSchema.relation`
> (`contracts:604-614`) is `broader-than`, `narrower-than`, `related-to`,
> `influences`, `contrasts-with`, `translation-equivalent`, `ritualizes`,
> `comments-on`, `shares-source-lineage`. A reader following the doc's
> vocabulary would write edges the contract rejects.

### `@nisaba/languages` — the polyglot philology engine

The richest real Nisaba code is the `@nisaba/languages` engine, barely
acknowledged in the prior docs. Its `src` holds 30+ script/language subdirs
(`hebrew`, `aramaic`, `syriac`, `arabic`, `ethiopic`, `phoenician`, `samaritan`,
`ugaritic`, `cuneiform`, `egyptian-hieroglyphic`/`hieratic`/`demotic`,
`ancient-greek`, `latin`, `coptic`, `devanagari`, `grantha`, `kharoshthi`,
`linear-b`, `old-church-slavonic`, `old-persian`, `pali`, `prakrit`, `runic`,
`tamil-brahmi`, `tibetan`, `classical-chinese`, `avestan`, …) plus
`transliteration`, `tokenization`, and a `lexicon` module. It exports concrete
script handlers — `ImperialAramaicScriptHandler`,
`BiblicalAramaicScriptHandler`, `SyriacScriptHandler`, `ArabicScriptHandler`,
`EthiopicScriptHandler`, `SamaritanScriptHandler`, `UgariticScriptHandler`,
`SumerianCuneiformHandler`,
`EgyptianHieroglyphicHandler`/`HieraticHandler`/`DemoticHandler` — far beyond
the "morphology lookup" the docs gesture at. `@nisaba/criticism` ships real IIIF
manifest linking (`iiif-linking.ts`: `IIIFLinkRegistry`, `IIIFManifestRef`).

### Search modes and cross-domain wiring

The shell adapter's `NisabaApiAdapter` (`libs/oshun/domain-nisaba/src/types.ts`)
exposes `getDailyPassage`, `getContinueReading`, `getSavedPassages`,
`save`/`unsavePassage`, `getStudyReminders`, `setStudyReminder`,
`toggleStudyReminder`, `getConceptThreads`, `getWorkspaceEntries`,
`searchLibrary`, and `getHealth`. Its search is multi-mode:
`NisabaSearchMode = fulltext | lemma | morphology | semantic | regex | proximity`
over
`NisabaSearchEntityKind = passage | source | concept | notebook | collection`,
and `NisabaResearchType` is `CRITICAL_EDITION` | `TRANSLATION` |
`COMPARATIVE_STUDY` | `PHILOLOGICAL_ANALYSIS` | `MANUSCRIPT_SURVEY`.

Cross-domain wiring is real: `concept-graph-linkages.ts` delegates to the shared
`@oshun/navigation` concept graph (`buildOshunSharedConceptGraphThread`,
`inferOshunSharedConceptIds`) through a `NISABA_CONCEPT_ID_ALIASES` map —
`'concept-attention' → 'focus-protection'`,
`'nisaba-concept-detachment' → 'honest-reflection'`,
`'nisaba-concept-nonreaction' → 'honest-reflection'` — and
`domain-recommendations.ts` builds cross-domain recs with reasons
`'shared_concept_graph'` | `'source_study'`. `@nisaba/study-plans` is
re-exported from `domain-nisaba/src/study-plans.ts` (`createNisabaStudyPlan`,
`recordNisabaLearnerProgress`, `sequenceNisabaStudyResources`,
`NisabaCompletionForecast`, `NisabaStudyPlanCadence`/`Difficulty`/`Objective`).

> **External-source configuration.** The Nisaba external-source endpoints are
> configured via env vars `NISABA_SEFARIA_API_URL`, `NISABA_CDLI_API_URL`, and
> `NISABA_IIIF_BASE_URL`, present in `.env.example` (lines 102-104:
> `https://www.sefaria.org/api`, the CDLI API, and a local IIIF base).
> Conceptual CDLI/Oracc ATF and IIIF handling also exists inside
> `@nisaba/languages` (cuneiform ATF) and `@nisaba/criticism`
> (`iiif-linking.ts`).

> **Honest state.** The canonical contracts, the `@nisaba/languages` engine, the
> shared concept-graph linkages, and `@nisaba/study-plans` are real. Per the
> 2026-06-23 triage, Nisaba is **disconnected** in the hydrated shell (with
> Metis `planned`, the post-hydration `shellDomainCount` drops to 4); the e2e
> `nisaba-scholarly-read` runs on a stub workspace API with render-only scholar
> apparatus, and `nisaba-notebook-capture-and-cite` has the Sophia stable-ID
> round-trip stubbed. Scholar entitlement-gated reading mode, durable annotation
> re-anchoring across edition revisions, and the source-lifecycle invalidation
> cascade are contract-modeled but runtime-unverified.

---

## Metis — Courses, Tutoring, and Adaptive Learning

**Purpose:** the educational substrate — courses, tutoring, BYOM study,
assessments, knowledge-graph promotion, and standards-based institutional
delivery. Metis is far more implemented than typical domain docs imply, yet it
is **not currently a live launch surface**: the registry sets
`availability: 'planned'` (`registry.ts:489`), and `getAvailableDomains()`
filters out `planned` domains. "Launch-blocking V1 scope" is roadmap intent, not
current launch state — the 2026-06-23 triage confirms the shell drops Metis to
`planned` post-hydration.

### Packages

- **Apps** — all five `apps/metis/{web,admin,api-gateway,worker,mobile}` exist
  (web 93 ts/tsx, admin 62, api-gateway 29, worker 21, mobile 6).
- **Adapter** — the `domain-metis` facade already exists:
  `libs/oshun/domain-metis/src/index.ts` (one line) re-exports `adapter.ts` (six
  lines), which re-exports `@metis/api-client` and aliases
  `createMetisDomainAdapter` / `createMetisDomainReadAdapterRegistry`. The
  substantive adapter is `libs/metis/api-client/src/adapter.ts` (~1,111 LOC).
  Consolidation is tracked under §1.3, but the thin facade is present today.
- **Domain libraries** (non-test LOC): `@metis/agents` (23.3 K),
  `@metis/knowledge-graph` (19.2 K), `@metis/integrations` (18.5 K),
  `@metis/assessment` (12.4 K), `@metis/adaptive` (8.5 K), `@metis/core` (6.1
  K), `@metis/learning` (5.5 K), `@metis/tutoring` (4.3 K), `@metis/course` (4.2
  K), `@metis/verification` (4.1 K), `@metis/discovery` (~1.5 K),
  `@metis/gradebook` (~0.85 K) — the last three (`gradebook`, `discovery`,
  `verification`) exist on disk but were absent from the prior library list.
- **Contracts** — `libs/contracts/src/metis/` re-exported as the
  `MetisContracts` namespace (`export * as MetisContracts from './metis'` at
  `libs/contracts/src/index.ts:103`).
- **BFF** — `apps/oshun/bff/src/routes/`: `metis-integrity.ts`
  (`POST /metis/integrity` adjudicate;
  `POST /metis/integrity/:verdictId/appeal`;
  `GET /metis/integrity/:verdictId/audit`; appeals workspace),
  `metis-tutor-memory.ts`
  (`POST/GET /metis/tutor-session-memories[/:sessionId]`),
  `admin-metis-byom-decision.ts`, `metis-search-seeds.ts`,
  `assistant-metis-handoff.ts`, `metis.ts`.
- **Surfaces** — `apps/oshun/web/src/app/metis/` ships `assessment`, `byom`,
  `courses` (incl. `courses/new`), `ingest`, `lesson`, `lessons`, `session`,
  `tutor`, and `upload`. The consumer-shell **review surface is
  `/operator/metis`** (`apps/oshun/web/src/app/operator/metis/page.tsx`) — there
  is no `/admin/metis` route in the consumer web app, and no
  `apps/oshun/web/src/app/domains/metis/` directory, even though the registry
  declares `route: '/domains/metis'`.

### The 11 canonical contracts

All eleven are real Zod schemas in `libs/contracts/src/metis/index.ts`:

| Contract                                                  | Line |
| --------------------------------------------------------- | ---- |
| `LearningSourceBundleSchema`                              | 251  |
| `GroundingPackSchema`                                     | 461  |
| `CourseBuildSchema`                                       | 643  |
| `LessonAssetBundleSchema`                                 | 978  |
| `PublicationPackageSchema`                                | 1226 |
| `TutorPersonaProfileSchema`                               | 1425 |
| `TutorSessionMemorySchema`                                | 1605 |
| `LearningObjectiveMapSchema`                              | 1706 |
| `AssessmentEvidencePackSchema`                            | 1845 |
| `LearningTelemetryStatementSchema` (xAPI/cmi5-compatible) | 2020 |
| `AcademicIntegrityVerdictSchema`                          | 2061 |

### Mastery, decay, and the 6-axis personalization manifold

The six mastery bands are codified verbatim in
`libs/metis/learning/src/mastery/evidence-requirements.ts`: `unintroduced` →
`introduced` → `developing` → `approaching` → `mastered` → `maintaining`, each
with a `prerequisiteBandFloor`, plus half-life decay in `decay.ts`
(`maintainingHalfLifeMultiplier`; `mastered` → `approaching` demotion). They are
also enumerated as `GradebookMasteryBandSchema`.

`LearnerPersonalizationStateSchema` (`metis/personalization.ts`) is a real Zod
manifold with **six axes**, each wrapped in a
`PersonalizationAxisControl { pinned, loosened, reset, rationale }` and
versioned `/^\d+\.\d+\.\d+$/`: `pace`; `modality`
(`read-first`/`watch-first`/`practice-first`/`dialogue-first`);
`prerequisitePath` (`minimal`/`scenic` plus recap); `scaffoldDensity`
(`hintLadderDepth` 0–8, `workedExamplesPerArc` 0–20); `framingRegister`
(`socratic`/`direct`/`narrative`/`formal-proof`); and `culturalFrame`.

### The six core disciplines are codified data

`libs/metis/learning/src/subject-taxonomy/subject-taxonomy.ts` codifies
`CoreMetisDiscipline = 'philosophy' | 'religion' | 'psychology' | 'neuroscience' | 'anthropology' | 'astronomy'`
alongside a `SUPPORTING_METIS_SUBJECTS` array, a `MetisSubjectRole` of
`'core_headline'` | `'supporting_scaffold'`, and a `SUPPORTING_TO_CORE_ANCHOR`
map (e.g. `cosmology → astronomy`, `culture → anthropology`) — a real anchor
taxonomy, not just prose.

### Assessment, knowledge tracing, and the hint ladder

IRT is real in `libs/metis/assessment`: `irt-models.ts` exports
`IRTModelFamily`, `IRTParameters` (difficulty/discrimination/guessing),
`IRT_BOUNDS`, `MLE_DEFAULTS`, `resolveIRTParametersForModel`,
`isIRTRecalibrationDue`; DIF monitoring via
`monitorDifferentialItemFunctioning`; and an information-gain adaptive item
selector in `generation/adaptive-selector.ts` (the real "CAT-aligned" behavior —
there is no separately named CAT module). Knowledge tracing is real in
`libs/metis/adaptive/src/path/`: `bkt.js` (Bayesian Knowledge Tracing,
Baum-Welch EM), `fsrs.js` (FSRS-5 DSR), `graph-knowledge-tracing.js`
(Nakagawa-style), and a unified `adaptive-knowledge-engine.js`. The tutor's hint
ladder (`libs/metis/tutoring/src/hints/hints.ts`) is concrete:
`HintLevel { SUBTLE, DIRECT, WORKED_EXAMPLE, ANSWER_REVEAL }` with
`HINT_REVEAL_PERCENTAGES = { 0.1, 0.3, 0.7, 1.0 }`.

`@metis/verification` (4 K LOC) ships the grounded-generation enforcement the
docs only gesture at: a `VerificationGate`, `composeP0Gate`,
`runVerifiedGeneration` (generate-verify-refine loop), and a judge-verifier
panel scorer. `@metis/knowledge-graph` (19 K LOC, 75 files) ships construction
(`entity-relation-extractor`, cycle/orphan detection), a `GraphRagRetriever`
(`defaultCommunitySummarizer`, multi-hop expansion, pgvector seed, governance
filter), GNN/temporal modules, and a `GraphRagBenchmarkSuite`.

### Academic integrity (Themis)

`AcademicIntegrityVerdict` uses verdict classes **`clear`, `inconclusive`,
`violation`, `severe`** (`index.ts:2063` — not the `policy_violation` /
`severe_violation` long forms the spec uses; the gradebook enum adds `pending`).
Its 10-signal taxonomy (`index.ts:2073`) is exactly: `paste_external`,
`offplatform_search`, `response_pattern_anomaly`, `generated_text_classifier`,
`tutor_output_in_submission`, `pace_anomaly`, `device_fingerprint_change`,
`proctor_observation`, `peer_collaboration_breach`, `teacher_flag`. **Two
distinct integrity-mode vocabularies coexist**: the `GradebookAssessmentMode`
(`assessed`/`formative`/`practice`/`exam`/`collaborative`, `gradebook.ts:25`)
and the `TutorSessionMemory.integrityMode` (`teach`/`hint`/`practice`/
`do-not-complete-for-me`, `index.ts:1557`).

### Gradebook with role-redacted views

`GradebookEntrySchema` (`gradebook.ts:121`) carries `learnerId`, `assignmentId`,
`attempt`, `courseId`, `score`, `scoreScale`, `scoreBasis`
(`rubric`/`irt_derived`/`completion`/`attempt_count`/`mixed`),
`masteryBandAtEmission`, `assessmentMode`, `integrityVerdict`, and an
`emissionTrigger` (`assignment_completion`/`mastery_transition`/
`integrity_verdict_change`/`manual_recompute`), with `correctsVersion`,
`visibility`, and `tenantBinding`. `GradebookCorrectionEvent` (type
`gradebook.correction`) and `viewGradebookEntryForRole()` (`gradebook.ts:276`,
with `DEFAULT_VISIBILITY_RULES` for `learner`/`teacher`/`guardian`/
`institutional_admin`) implement the rights-and-visibility story concretely.

### BYOM safety: two separate frameworks

There are **two BYOM frameworks**, and the prior docs cited the wrong one for
model safety:

- `byom.ts` — bring-your-own-**material** ingestion connectors (file/url/feed/
  lms-import sources).
- `byom-model.ts` — the tenant-provided-**model** safety harness:
  `class ByomModelFramework` (~700 LOC) with `ByomEndpointStatus`
  (`active`/`quarantined`/`fail_closed_killed`/`fail_closed_quarantined`/…),
  `ByomKillSwitchSource` (`platform_operator`/`tenant_operator`),
  `ByomPolicyInterlock` (Lilith/Isis/Sophia gates), `ByomAbuseMonitor`,
  `DEFAULT_BYOM_ABUSE_THRESHOLDS`, `HIGH_RISK_TAGS`, `ByomTimeoutError`, and
  `MemoryByomAuditStream`.

### Standards-based delivery

Real in `libs/metis/integrations/src/standards/institutional-delivery.ts`:
`exchangeLtiLaunchToken`, `createLtiDeepLinkResource`, `dryRunOneRosterImport`,
`mapLearnerActivityToXapi`, `createScormFallbackManifest`, `exportQti3Item`,
`importQti3Item`, `mapLearnerActivityToCaliper`, `issueOpenBadgeCredential`,
`requiredMetisV1Standards`. LTI 1.3 id_token/JWKS verification is real in
`libs/shared/inbound-integrations/src/lti-verification.ts`
(`VerifyLtiIdTokenInput`, `LtiJwks`, `buildLtiAuthenticationRequestUrl`,
`generateLtiLoginState`); SCORM-2004 RTE in `scorm-2004-rte.ts`; OneRoster in
`oneroster.ts`; and SAML/OIDC/SCIM identity in `identity.ts`. All inbound
plumbing lives under `libs/shared/inbound-integrations/src/` (see
[Foundations](./foundations.md)).

### The adaptive learning loop

Every item runs the same loop: present → collect → build evidence → update
mastery if a band transition fires → update the personalization manifold → run
Themis signals → route through teacher/operator review if flagged → select the
next item from the bank, KG, and manifold → emit telemetry (xAPI / cmi5 /
Caliper / `GradebookEntry`). The stated **P95 ≤ 600 ms** response-to-next-item
budget is a spec assertion — no enforcing runtime gate was found in the code.

```mermaid
flowchart TB
    item["Present item<br/><sub>tutor selects from item bank + KG + manifold</sub>"]
    resp[Collect response + telemetry]
    ev["Evidence build<br/><sub>AssessmentEvidencePack</sub>"]
    mast{Mastery transition?}
    band["Update MasteryTransition<br/><sub>banded state · decay scheduled</sub>"]
    pers["Personalization update<br/><sub>pace · scaffold · modality · framing</sub>"]
    themis{Themis signals?}
    verdict["Run classifier policy<br/><sub>per-mode · severity → action</sub>"]
    teacher["Teacher / operator review<br/><sub>appeals + bias monitor</sub>"]
    next["Next-item select<br/><sub>information gain · prereqs · manifold</sub>"]
    emit["Emit telemetry<br/><sub>xAPI · cmi5 · Caliper · GradebookEntry</sub>"]

    item --> resp --> ev --> mast
    mast -- band changes --> band --> pers
    mast -- no change --> pers
    pers --> themis
    themis -- signals raised --> verdict
    themis -- clear --> next
    verdict --> teacher
    teacher --> next
    next --> emit
    emit -. loop .-> item

    classDef policy fill:#fee2e2,stroke:#991b1b,color:#7f1d1d
    classDef ai fill:#dbeafe,stroke:#1e40af,color:#1e3a8a
    classDef store fill:#f3e8ff,stroke:#6d28d9,color:#3b0764
    class verdict,teacher,themis policy
    class ev,pers,next ai
    class emit store
```

### Cross-domain wiring and storage gaps

Canonical cross-domain wirings present in `libs/metis/integrations/src/` are
`aja`, `iris`, `isis`, `lilith`, `psyche`, `sophia`, `themis`, `yemaya`
(`-canonical-wiring.ts`). The Tara, Nyx, Veritas, Nisaba, and Arete wirings the
spec claims **do not exist** as wiring files. Two further honest gaps: there is
**no dedicated `metis` PostgreSQL database** — `POSTGRES_MULTIPLE_DATABASES`
(`docker/docker-compose.dev.yml`) is
`yemaya,lilith,isis,iris,sophia,hathor, bellona,calliope,tara,maat,nisaba,shakti,cybele,kalika,lakshmi,athena,oya`,
with `metis` absent — so Metis's storage strategy (shared `oshun_dev` vs domain
tables) is undocumented; and the BYOM headline ingest-to-course pipeline is
rated **partial/uncovered** in the 2026-06-22 completeness audit. For reference,
the launch locale set is `OSHUN_LAUNCH_LOCALES`
(`libs/oshun/i18n/src/index.ts:26`): `en-US`, `es-US`, `fr-FR`, `de-DE`, `ar`,
`he`, `ja-JP`, `pt-BR`.

---

## Related

- [High-Level Architecture](./high-level-architecture.md) — where the six
  domains sit relative to substrates and the shell.
- [Product Surfaces](./product-surfaces.md) — the shell, consumer, and operator
  surfaces these domains render into.
- [Sophia — Grounding Substrate](./substrate-sophia.md) — grounds every Veritas
  claim and Metis lesson, and re-grounds on retraction.
- [Lilith — Contemplative Policy Substrate](./substrate-lilith.md) — Tara tone
  review and the mood crisis hand-off.
- [Living Scenes](./living-scenes.md) — the Contemplative-Arc cards and Arete
  Living Offerings.
- [Cross-Domain Support](./cross-domain-support.md) — the shared concept graph
  and bridge contexts that link the domains.
- [Search, Discovery, and Knowledge Graph](./search-discovery-knowledge-graph.md)
  — Nisaba/Metis search and KG promotion.
- [`../ARCHITECTURE.md`](../ARCHITECTURE.md) — the architecture hub.
- [`V1/features.md` § Editorial Workflow, Source Verification, and Contradiction Detection](../features.md#editorial-workflow-source-verification-and-contradiction-detection)
- [`V1/features.md` § Adaptive Learning Loop, Themis Adjudication, and BYOM Safety](../features.md#adaptive-learning-loop-themis-adjudication-and-byom-safety)
