Arete is the goals, habits, discipline, reflection, and coaching domain of Oshun V1 — the place a member declares who they are trying to become, checks in against that intention day by day, and is met with humane recovery rather than shame when life interrupts the rhythm. It serves the self-directed practitioner who wants structure without a punishing streak counter, and it sits inside the single Oshun shell alongside the contemplative domain (Tara — Rituals and Contemplative Practice), the grounded-claims domain (Veritas — Grounded Stories and Claims), and the assistant. Among the V1 domains it is one of the most code-complete: the data model and the core recovery and friction algorithms are real and domain-specific, while a handful of end-to-end UI and persistence seams — habit wizard cross-device sync, the Living Offerings intention-capture textarea, and the session→streak write — are still in progress.
This page is a product-and-code reading of Arete. The completion state of every
item lives in ../TODOS.md; cross-domain build ordering lives in
../DEPENDENCIES.md. The companion runtime view is
Customer Domains, and the assistant-side
crisis pre-screen that wraps Living Offerings is described in
Lilith Persona Policy.
Where Arete lives in the product#
Consumer surfaces#
The consumer hub is /arete, with presentational depth rendered by the
Lilith-design-system AreteRoom and its variants. The real
apps/oshun/web/src/app/arete route tree contains eleven directories:
coaching— the weekly synthesis cardgoal— single-goal detailhabits— habit list and the create wizardoffering(singular) — the Living Offering composition surfaceofferings(plural, a distinct directory) — the kept-offering gallery and send/keep actionspatterns— pattern analysis across time-of-day, day-of-week, mood, etc.plan— daily/weekly plan viewrecovery— the humane miss-recovery flowreview— the weekly review workspacestreak— the streak visualizationweekly— the weekly review entry point
The original feature list at
V1/features.mdenumerated onlycoaching/goal/offering/patterns/plan/recovery/review/streak. The real tree addshabits/,offerings/(plural — separate from the singularoffering/), andweekly/. The singular/plural split is intentional:offering/is where you compose a Living Offering;offerings/is where you keep and revisit them.
Power-user deep tools#
A separate, twelve-surface power-user tree lives at
apps/oshun/web/src/app/domains/arete: affirmations, balance, coach,
gamification, goals, habits, journal, plan-review, progress,
seven-habits, time, and vision. These are the deeper authoring and
analysis tools that sit behind the calmer consumer room.
Service and library tiers#
| Tier | Location | Role |
|---|---|---|
| Contracts | libs/contracts/src/arete/index.ts (1,207 lines) |
Canonical Zod schemas; re-exported via export * from './arete' at libs/contracts/src/index.ts:22 |
| Domain adapter | libs/oshun/domain-arete/src |
Recovery engine, friction taxonomy, card model, cross-domain linkages, persistence round-trip |
| Service apps | apps/arete/{api,mobile,web} |
Service-side scaffolding (api/, mobile/, web/ subdirs) |
@arete/* libraries |
libs/arete/* |
13 packages: affirmations, ai-coach, api-client, balance, core, database, gamification, goals, habits, journal, seven-habits, time, vision |
| BFF | apps/oshun/bff/src/routes/arete.ts and apps/oshun/bff/src/arete/* stores |
Member-scoped HTTP surface and persistence |
A note on where persistence actually lives: the companion architecture text
once implied apps/arete/* is the persistence tier. In practice, the
member-scoped persistence the audit exercises lives in the BFF stores —
apps/oshun/bff/src/arete/arete-review-store.ts, arete-offering-store.ts, and
arete-coach-decision-store.ts — together with a client-side useAreteStore
backed by localStorage['oshun.arete']. apps/arete exists with api/,
mobile/, and web/ subdirectories, but it is not the sole or primary
persistence path for the V1 consumer surfaces.
What Arete includes (V1)#
- Canonical models for habits, goals, routines, check-ins, journals, plans, coaching summaries, streaks, missed days, friction signals, interventions, and recovery records.
- Humane streak recovery and missed-day handling (real algorithms, not stubs).
- Mobile daily check-in, goals, routines, recovery, accountability, and progress.
- Web plan/review workspace, journaling, reflection, progress maps, pattern analysis, and weekly review.
- Coaching summary cards, continuity cards, save/share/export behavior, and next-practice recommendations.
- Cross-domain links to Tara ritual suggestions, Nisaba study prompts, Veritas evidence or habit-science explainers, assistant accountability, and shared concept-graph themes.
- Tests for streaks, recovery, plan state, persistence, charts, accessibility, weekly review, daily check-in, and recovery flows.
The check-in: status, engagement, and streak treatment#
The single most important contract in Arete is the check-in — one entry per
habit per local day — because it is where the no-shame philosophy is enforced
in code, not just described in copy. CheckInStatusSchema (index.ts:10) has
exactly five values:
CheckInStatusSchema = z.enum(['done', 'partial', 'skip', 'decline', 'miss']);
Every status maps deterministically to a streak treatment through the
CHECK_IN_STREAK_TREATMENT constant (index.ts:1179-1185), and the check-in's
own streakTreatment field is validated against that map — a member or client
cannot record a done check-in that secretly counts as no-count:
| Status | Meaning | Streak treatment | Engagement | Reason required? |
|---|---|---|---|---|
done |
Completed at intent | engaged |
exactly 100 |
no |
partial |
Completed under intent (still counts) | engaged |
> 0 and < 100 |
no |
skip |
Deliberately not done today | grace |
exactly 0 |
yes (statusReason) |
decline |
Refused with a stated reason | grace |
exactly 0 |
yes (statusReason) |
miss |
Not engaged | no-count |
exactly 0 |
no |
Two helpers expose the same logic to the rest of the system:
getCheckInStreakTreatment(status) returns the treatment, and
isEngagedCheckInStatus(status) returns true only for engaged treatments
(done/partial).
The superRefine invariants#
The CheckInSchema.superRefine (index.ts:480-529) makes these rules
structural. The validation rejects:
- a
donecheck-in whoseengagementPercent !== 100; - a
partialwhose engagement is<= 0or>= 100; - any
skip/decline/misswhose engagement is not0; - a
skipordeclinewith anullstatusReason(the no-shame floor still asks for a visible reason, so the member owns the choice); - a
streakTreatmentthat disagrees withgetCheckInStreakTreatment(status).
This is why the streak treatment is described as "enforced by a status→treatment
table with superRefine validation" rather than computed ad hoc per surface. The
check-in also carries a MoodSnapshot (valence −2..2, arousal 0..4),
energy (1..5), frictionNotes, and evidenceRefs (typed AreteReferences
into other domains), so a single tap captures enough context for pattern
analysis without a separate logging step.
Habits, goals, and routines#
Habit schema#
HabitSchema (index.ts:270) is owner-scoped (userId) and carries
declaredIntent, a declaredDifficulty from
DeclaredDifficultySchema = ['tiny','easy','moderate','hard','stretch'], a
declaredCadence, prerequisiteHabitIds, an optional linkedGoalId and
linkedRoutineId, and — critically — its own humaneStreakPolicy. Invariants:
a habit cannot list itself as a prerequisite, and an inactive habit must carry
an archivedAt timestamp (you cannot silently deactivate a habit without
recording when).
HabitCadenceKindSchema has five values: daily, weekly, count-per-period,
on-trigger, custom. The cadence is modeled by DeclaredCadenceSchema
(index.ts:214):
| Field | Type / range | Notes |
|---|---|---|
kind |
HabitCadenceKind |
drives the conditional invariants below |
daysOfWeek |
int[] 0–6, max 7, unique | required non-empty for weekly |
targetCount |
int 1–99 | null | required for count-per-period |
period |
day|week|month | null |
required for count-per-period |
triggerRef |
string | null | required for on-trigger |
graceWindowHours |
int 0–168 | the per-habit grace window |
Goal schema#
GoalSchema (index.ts:402) carries a timeframe from GoalTimeframeSchema
(daily, weekly, monthly, quarterly, annual, multi-year,
open-ended) and a scope from GoalScopeSchema — ten values: identity,
health, learning, work, relationship, spiritual, financial,
community, creative, custom. A goal requires between 1 and 12
measurableCriteria (each an AreteMetric with a direction of
at-least/at-most/exact), supports subGoalIds, linkedHabitIds, and a
parentProjectId, and validates its lifecycle through GoalCompletion:
completed goals require completedAt, abandoned goals require abandonedAt,
and a targetDate cannot precede the startDate.
Routine schema#
RoutineSchema (index.ts:322) is an ordered sequence of RoutineSteps with a
timeOfDayWindow (startLocalTime/endLocalTime/timezone), a declared
duration, and substitutionRules. Invariants enforce unique step IDs and orders
as well as distinct window start/end times. They also ensure that required
(non-optional) steps cannot exceed the declared duration and that disabled
substitution rules cannot allow substitutions per run.
Humane streak and recovery policy#
Arete's streaks track engagement, not punishment. The UI never says "you broke your streak." Two layers make this real: a per-habit policy contract and a recovery engine that interprets misses against it.
The policy contract#
HumaneStreakPolicySchema (index.ts:261) is attached to every habit:
| Field | Type / range | Meaning |
|---|---|---|
skipPreservesStreak |
boolean | does a skip hold the streak? |
declinePreservesStreak |
boolean | does a decline hold the streak? |
missGraceCadences |
int 0–30 | how many missed cadences are forgiven |
recoveryPromptAfterMisses |
int 1–30 | when a recovery invitation surfaces |
visualLanguage |
no-shame | neutral |
the streak's emotional register |
On the "24 h / 72 h" grace figures.
V1/features.mdstates a fixed "24 h grace on daily habits; 72 h grace on weekly cadence." Those are spec defaults for product copy, not hard-coded constants. In code, grace is configurable per habit viaDeclaredCadence.graceWindowHours(0–168) and the policy'smissGraceCadences. Read the 24/72 figures as illustrative defaults, not engine constants.
The recovery engine#
libs/oshun/domain-arete/src/streak-recovery.ts (≈21 KB, 645 lines) is the real
algorithm, not a stub. Its key exports:
evaluateAreteHabitRecovery(input)(streak-recovery.ts:111) — takes a habit, its current/longest streak, last-completed timestamp, andnow, and returns anAreteHabitRecoveryPlanwith arecoveryStage(e.g.on_track,grace_window,repair_window,fresh_restart), astreakDisposition(intact/protected/archived), a no-shamesummary, acoachingNudge, and areasoningtrail. It computes missed windows, the freeze allowance remaining, the recovery-window end, and the next celebration day.countMissedHabitWindows(input)(streak-recovery.ts:371) — cadence-aware miss counting that branches ondaily,weekdays,weekends,weekly,biweekly,monthly,quarterly,annual, andcustom. It returns0when there is no last completion or when the last completion is in the future.applyAreteHabitRecoveryCompletion(input)(streak-recovery.ts:317) — applies a completion to a plan. In arepair_window/fresh_restartit restarts the live streak at1and keeps the prior run archived as momentum instead of erased; inside an intact/protected stage it advances the streak by one day.ARETE_HUMANE_STREAK_POLICY— the engine's default policy:freezeAllowancePerQuarter: 1,recoveryGraceDays: 2,celebrateAtDays: [7, 21, 50, 100].
The escalation ladder the engine encodes matches the product spec: a small miss inside the grace window prompts a single clean repetition ("no backfilling, no apology loop, no doubling up"); a deeper pattern moves toward a repair window or fresh restart that archives rather than zeroes prior momentum. Plan re-scoping (lowering difficulty or shrinking scope) is offered, never imposed — the member is always in control. Drift is surfaced to the member and the coaching summary, never to leaderboards or social.
Missed days, streaks, and recovery records#
These three contracts are the spine of the humane-recovery story and are worth naming explicitly.
MissedDaySchema (index.ts:533) records a window that lapsed. It requires at
least one of habitId/routineId/goalId, a graceExpiresAt timestamp, a
disposition from MissedDayDispositionSchema (within-grace, logged-miss,
recovered, excused), a streakTreatment of grace or no-count, and
userVisibleCopy. The invariants enforce the humane contract directly:
- a
recoveredmissed day requires a linkedrecoveryRecordId; within-graceandexcuseddispositions must keepstreakTreatment === 'grace';- a
logged-missrequires a visiblestatusReasonand ano-counttreatment — you cannot log a miss against someone silently.
StreakRecordSchema (index.ts:811) tracks currentEngagementCount,
longestEngagementCount, forgivenessDaysUsed/forgivenessDaysAllowed,
missPatternCount, a visualState from
StreakVisualStateSchema = ['steady','grace','drift','recovery','paused'], and
a log of StreakEvents. Invariants prevent the current count from exceeding the
longest, prevent lastEngagedOn from preceding startedOn, and prevent used
forgiveness from exceeding the allowance.
RecoveryRecordSchema (index.ts:857) models the recovery flow itself: a
stage from RecoveryStageSchema (invited, accepted, in-progress,
completed, declined, expired), a target, a trigger
(missed-day/friction-signal/manual/coaching-summary), and an
invitation carrying a tone, optional taraPracticeRef, and rescope options.
Non-manual recovery records require evidence triggers; accepted / declined /
completed stages each require their corresponding timestamp; and a record cannot
be both accepted and declined.
PlanSchema (index.ts:733) ties recovery to forward commitment. A plan has a
scope (daily, weekly, recovery, goal-rescope, routine-adjustment),
commitments (PlanCommitments the member explicitly accepts via
acceptedByUser), and links to its sourceReviewId/sourceSignalIds. Its
invariants include the load-bearing one: a recovery-scoped plan requires a
recoveryRecordId, and a superseded plan requires a supersededByPlanId.
Friction, interventions, and pattern analysis#
Friction signals (11 kinds) and interventions (9 kinds)#
FrictionSignalKindSchema (index.ts:59-71) has eleven values, richer than
the prose in V1/features.md conveys:
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 (index.ts:74) has nine: 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 kind is the explicit bridge from a habit friction into a
Tara contemplative reset — coaching can prescribe a ritual,
not just a rescheduling.
A FrictionSignal (index.ts:965) carries a source (check-in, journal,
calendar, assistant, veritas, metis, tara, nyx, manual), a
severity, suggestedInterventionKinds, and a privacy block; its invariant
enforces that an accountability-visible signal must also be coach-visible.
InterventionSchema (index.ts:1002) models the lifecycle (proposed,
accepted, declined, applied, dismissed, expired) with per-status
timestamp invariants and a governance gate: if userApprovalRequired is set,
an accepted or applied intervention must be userApproved.
The domain-side friction taxonomy#
libs/oshun/domain-arete/src/friction-taxonomy.ts (≈19 KB) is a full, versioned
taxonomy — ARETE_FRICTION_TAXONOMY_VERSION = '1.0.0'. It exports
ARETE_FRICTION_DESCRIPTORS (a Record keyed by twelve AreteFrictionCategory
values such as time_fragmentation, scope_overload, emotional_resistance,
recovery_debt, meaning_drift, attention_residue, consistency_gap, each
with a phase, default signals, and default interventions) and
ARETE_INTERVENTION_DESCRIPTORS (keyed by twelve AreteInterventionKind values
such as scope_reduction, cue_redesign, recovery_support,
momentum_rebuild, each with an intensity and compatible practice moments).
The functions buildAreteInterventionRecommendations(...) and
summarizeAreteFrictionLogs(...) turn logged friction into recommended
interventions and an AreteFrictionSummary. This is a genuine domain taxonomy
with named categories and constants — the opposite of a renameable CRUD stub.
Coaching summary and pattern analysis#
CoachingSummarySchema (index.ts:1103) is structured, not free text. Its
observedPatterns array carries a kind enum — time-of-day, day-of-week,
mood-correlation, habit-interaction, seasonality, location, post-event
— plus a supportBand of weak/moderate/strong and at least one
evidenceRef per pattern. The summary also has a tone
(invitational/direct/reflective/celebratory/recovery),
celebratedWins, suggestedAdjustments (typed interventions), invitations
(not prescriptions), and a governance block with a memoryScope
(session/profile/notebook/tenant) and the invariant that an
accountability-visible summary requires a consentRecordId. The pattern
analysis dimensions named in the spec map exactly onto the observedPatterns
kind enum.
Weekly review and reflection#
The weekly review is a real schema, not just a copy structure.
WeeklyReviewSectionSchema (index.ts:633) ships the exact four quadrants the
spec names at V1/features.md:
WeeklyReviewSectionSchema = z.object({
celebrate: z.array(...).max(12), // what went well
notice: z.array(...).max(12), // what patterns emerged
choose: z.array(...).max(8), // what to commit to next week
invite: z.array(...).max(8), // what to release
});
WeeklyReviewSchema (index.ts:641) wraps those sections with a status
(scheduled/started/completed/skipped), a cadence
(sunday-evening/custom), an inputs block
(habit/goal/routine/check-in/journal IDs plus crossDomainRefs),
planAdjustments (each with userApproved), and nextPracticeRecommendations.
Its invariants require completedAt on a completed review and a skippedReason
on a skipped one — skipping a review is itself logged but never penalized,
which is the no-shame floor applied to the review ritual itself.
Inputs are habit/goal/routine logs, mood, energy, journal entries, and cross-domain context. Output is a continuity card, next-practice recommendations, and plan adjustments saved only with the member's approval. Cadence defaults to Sunday evening but is configurable.
Cross-domain links#
Arete is deliberately porous to its neighbors, via typed AreteReferences
(domain ∈ arete/tara/veritas/nyx/nisaba/metis/sophia/iris) and
the domain-adapter relationship modules (tara-relationship.ts,
nisaba-relationship.ts, metis-relationship.ts, veritas-habit-grounding.ts,
concept-graph-linkages.ts):
- Tara recovery rituals surface on missed days (and via the
tara-ritual-surfacingintervention). - Nisaba reflection passages and study prompts surface on stuck patterns — see Nisaba — Scholarly Study.
- Veritas habit-science explainers surface on declared interest — see Veritas — Grounded Stories and Claims.
- Assistant accountability is offered with consent — see Assistant Experience.
- Concept-graph theme alignment ties habits and goals to shared themes.
BFF surface and persistence#
registerAreteRoutes(app) (apps/oshun/bff/src/routes/arete.ts:137) mounts the
member-scoped HTTP surface. The adapter-capability routes include
/v1/arete/adapter/capabilities, /availability, /home-cards,
/continue-items, /search, /launch, /active-goals, and
/bridge-commitments; the practice and coach surfaces add
/v1/arete/practice/home and POST /v1/arete/coach/responses. Beyond these,
the room and offering/review surfaces (exercised by the BFF stores and the
AreteOfferingActions / AreteReviewClose web components) expose:
| Endpoint | Purpose |
|---|---|
/v1/arete/room |
the room-state composite |
/v1/arete/habits (+ /:habitId/check-in) |
habit list and per-habit check-in |
/v1/arete/offerings/keep |
keep a composed Living Offering |
/v1/arete/offerings/sent |
offerings shared/sent |
/v1/arete/review/close |
close a weekly review |
/v1/arete/review/closed |
closed-review history |
Persistence is layered: BFF stores (arete-review-store.ts,
arete-offering-store.ts, arete-coach-decision-store.ts) plus a client-side
useAreteStore over localStorage['oshun.arete']. The most recent persistence
work is commit 65e3f609e0, "test(arete): persist habit wizard creations".
Living Offerings#
Arete is the home of Living Offerings: a member states an intention and receives a 4–8 minute Living Scene tuned to it, watermarked, kept in their gallery as a personal artifact, and re-renderable forever from the underlying score. Sharing is opt-in and governed by the Living Scenes shareability matrix; the private intent layer never leaves the originating member. The full surface, the Arete template specification, and the keep/share/cascade rules are described in Living Scenes — Concept and Customer Promise and Keep, Share, Shareability, Takedown, and Lineage. Because intention capture can carry distress, the Living Offerings create flow is wrapped by a Lilith crisis pre-screen — see Lilith Persona Policy.
What is real, and what is still in progress#
Real and domain-specific. The data model and core logic are the strong core:
the check-in status→treatment table is enforced by superRefine; the
MissedDay/RecoveryRecord/Plan invariants encode the humane-recovery
contract structurally; evaluateAreteHabitRecovery and
countMissedHabitWindows are real cadence-aware algorithms; and the friction
taxonomy is a full, versioned (1.0.0) catalog with named categories,
descriptors, and recommendation logic.
Partial / in progress (per the 2026-06-22 completeness audit). Four items
are rated partial: arete-create-habit, arete-living-offering-create,
weekly-review-arete, and arete-streak-recovery. Concretely:
- the habit wizard create flow POSTs to the real BFF (commit
65e3f609e0), but cross-device sync is not yet covered; - the living-offering intention capture is still hardcoded JSX rather than a real textarea;
- the session→streak write and the grace-window timing remain to be wired.
In short: the contracts and the recovery/friction engines are production-grade;
some end-to-end UI/persistence seams and the full Living-Offerings render
pipeline are not yet complete. This page reports that honestly rather than
claiming a finished surface — the backlog at ../TODOS.md is the
authority on completion state.
Related#
- Tara — Rituals and Contemplative Practice — the
contemplative domain that recovery rituals and
tara-ritual-surfacingbridge into - Veritas — Grounded Stories and Claims and Nisaba — Scholarly Study — the cross-domain explainers and study prompts Arete links to
- Living Scenes — Concept and Customer Promise and Keep, Share, Shareability, Takedown, and Lineage — the Living Offerings pipeline and share rules
- Lilith Persona Policy — the crisis pre-screen wrapping Living Offerings intention capture
- Assistant Experience — consent-gated accountability
- Customer Domains — the companion runtime view of Arete's contracts and surfaces
- Subsystem Glossary — shared vocabulary
../features.md— feature hub