Oshun Platform · Architecture

Customer-Facing Domains

A focused page within the Oshun Platform Architecture documentation. The full map and every sibling page live in the Architecture hub.

8sections32 minread1diagram10tables

On this page

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, Iris, Lilith, Psyche, Isis). This page is the architectural reference for those six verticals; it sits below the High-Level Architecture and beside Product Surfaces, 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#

  • Contractslibs/contracts/src/tara/ (re-exported from @oshun/contracts).
  • Domain adapterlibs/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 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); 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#

  • Contractslibs/contracts/src/arete/.
  • Domain adapterlibs/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 tierapps/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.
  • BFFapps/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.
  • Surfacesapps/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 valuestime-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 valuesnotification-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, 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.tsVeritasApiAdapter 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.

Surfacesapps/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-supportedsupportedemergingcontestedunsupportedretracted.

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-attestationpublished 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#

  • Contractslibs/contracts/src/nyx/ (re-exported via export * from './nyx').
  • Domain adapterlibs/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 treelibs/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.
  • BFFapps/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 (g1g5), 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#

  • Contractslibs/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.
  • Surfacesapps/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.
  • Contractslibs/contracts/src/metis/ re-exported as the MetisContracts namespace (export * as MetisContracts from './metis' at libs/contracts/src/index.ts:103).
  • BFFapps/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.
  • Surfacesapps/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: unintroducedintroduceddevelopingapproachingmasteredmaintaining, each with a prerequisiteBandFloor, plus half-life decay in decay.ts (maintainingHalfLifeMultiplier; masteredapproaching 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).

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.

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 databasePOSTGRES_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.