# Journey: Broken Arete streak to humane recovery

The streak goes quiet. After three quiet days, Lilith checks in — with a small
offer, not a debt. Verifies that the
[Humane Streak and Recovery Policy](../../V1/features.md#humane-streak-and-recovery-policy)
holds end-to-end: the recovery surface uses the `coach` persona's hard ban on
"shaming language, escalating-stake framing, punitive streak rhetoric"
([`V1/features.md#persona-taxonomy`](../../V1/features.md#persona-taxonomy-tone-band-catalog-and-crisis-recovery-journey)),
the streak surface continues to render with the existing "promise" copy, and the
recovery offers are framed as "not a debt" — not as catch-up debt to be repaid.

## Personas

- **Daily-practice user, one missed day** — within the 24h grace window; no
  visible change yet, no recovery prompt
- **Daily-practice user, three quiet days** — qualifies for the recovery
  check-in surface at `/arete/recovery`
- **User with seven+ quiet days** — qualifies for the deep re-engagement flow
  with goal re-evaluation
- **User who explicitly declined accountability prompts** — drift detection must
  still flag to the coaching summary, never to leaderboards or social per the
  no-shame floor
- **User in a crisis frame** — recovery prompts and humane check-ins are
  suspended in favor of safety surfaces; verifies the
  [crisis policy supersedes](../../V1/features.md#crisis-aware-behavior) the
  Arete recovery flow

## Pre-conditions

- User signed in; middleware permits `/arete`, `/arete/streak`, and
  `/arete/recovery`
- `getArete()` reads the member's `/v1/arete/room` state and returns an honest
  unavailable room rather than fallback streak fixtures when the feed fails (see
  [`customer/04-arete/arete.md`](../customer/04-arete/arete.md))
- `getAreteStreak()` returns `AreteStreakData` with `streakCount`, `streakNote`,
  `weekHeatmapStartDateIso`, `weekHeatmap`, `promise`, `todaysInvitation` (see
  [`customer/04-arete/arete-streak.md`](../customer/04-arete/arete-streak.md))
- `getAreteRecovery()` returns `AreteRecoveryData` with `quietDaysLabel`,
  `welcomeHeadline`, `welcomeBody`, `noticed`, `offers`, `journalPlaceholder`
  (see
  [`customer/04-arete/arete-recovery.md`](../customer/04-arete/arete-recovery.md))
- Lilith `coach` persona is the active persona on Arete surfaces per
  [`V1/features.md#persona-taxonomy`](../../V1/features.md#persona-taxonomy-tone-band-catalog-and-crisis-recovery-journey);
  tone band is `reflective`
  ([`V1/features.md#tone-band-catalog`](../../V1/features.md#persona-taxonomy-tone-band-catalog-and-crisis-recovery-journey))
- No Lilith crisis frame is active (the crisis policy supersedes — see the
  failure modes below)

## Steps

### 1. User opens Arete on a quiet day

- [ ] Navigate to `/arete` — `AreteRoom` renders the intention rows, week stats,
      evening prompt, and "The coach · this week" card (per
      [`customer/04-arete/arete.md`](../customer/04-arete/arete.md))
- [ ] Each intention row shows a streak chip eyebrow and a 7-day cell bar
- [ ] On a single quiet day, the streak chip still reads the prior count; 24h
      grace window applies per
      [`V1/features.md#humane-streak-and-recovery-policy`](../../V1/features.md#humane-streak-and-recovery-policy)
- [ ] Copy MUST NOT say "you broke your streak" anywhere — this is the no-shame
      floor

### 2. User opens the streak surface

- [ ] Click into the streak surface at `/arete/streak` — per
      [`customer/04-arete/arete-streak.md`](../customer/04-arete/arete-streak.md),
      the page wraps a PhoneFrame inside an `LWebShell` with the masthead kicker
      "A practice, gently kept" and title "The _streak._"
- [ ] The lede card carries the promise copy: "A streak should never punish a
      Sunday off. Two rest days a week are part of the practice. Three quiet
      days in a row, and Lilith will check in — never before."
- [ ] The phone canvas renders the day numeral, the streak count, the
      `streakNote` ("Two quiet days are folded into the count, as we agreed."),
      the 42-cell heatmap, and the "promise" callout card
- [x] Bottom action row shows `Adjust` (ghost, hrefs to
      `/arete/plan?intent=adjust`) and `Begin` (primary, hrefs to
      `/tara/sit/when-the-mind-wanders`) — wired 2026-05-26.
- [x] Heatmap cell colors derive from state (`kept` = accent, `half`, `rest`,
      `today` = ink with 2px accent border, else `rule2`), and each cell exposes
      a date + state accessible label — verified 2026-06-24.
- [ ] No copy on the streak surface uses guilt or punishment language

### 3. After three quiet days, recovery surfaces

The trigger is the recovery-prompts rule in
[`V1/features.md#humane-streak-and-recovery-policy`](../../V1/features.md#humane-streak-and-recovery-policy):
"after 3, recovery-focused content (Tara recovery practice, Arete plan
re-scope)".

- [ ] On the user's next visit, the recovery surface at `/arete/recovery` is
      reachable (per
      [`customer/04-arete/arete-recovery.md`](../customer/04-arete/arete-recovery.md)
      Entry points: a push notification, a link from `/arete/streak`, or a home
      rail "soft return" CTA)
- [x] The real `/v1/arete/streak` recovery engine honors the grace threshold:
      two missed windows stay in `grace_window` with an `intact` disposition and
      "Recover with one repetition"; the third missed window crosses into
      `freeze_window` with `protected` disposition and "Protect the streak and
      return" (`arete-streak-recovery`, 2026-06-29). The browser
      `/arete/recovery` route itself is still static/reachable rather than gated
      by this engine state.
- [ ] The masthead reads "The soft return" / "_Welcome_ back."
- [ ] The lede block describes the surface: "Three days went quiet. Lilith names
      what she observed (the recital, the long workdays), then offers three
      returns at three sizes. None are a debt."

### 4. Recovery surface renders the humane copy

- [ ] Phone canvas shows the `quietDaysLabel` accent eyebrow ("three quiet
      days")
- [ ] Welcome headline italic + body lead/emphasized/rest spans render
- [ ] "What Lilith noticed" callout card with accent left bar uses
      `data.noticed` (the recital, the long workdays — names what's been
      observed without inferring blame)
- [ ] "An offer · not a debt" stack — three offer cards from `data.offers`
      (first one styled as primary)
- [x] One-sentence written option ("what was it like?") renders a real labelled
      textarea, character count, submit gate, and saved/error status;
      `Save note` POSTs the browser-entered note to `/v1/arete/recovery/journal`
      — verified 2026-06-26.
- [x] Bottom action row has `Begin · 6 min` primary, linked to the first return
      offer's Tara sit target — verified 2026-06-24.

### 5. User chooses an offer

- [x] The three offers correspond to three sizes (e.g., "A 6-min sit, the walk
      only, or a written one-sentence" per the page caption)
- [x] User taps one — the 6-minute offer navigates into the real Tara sit route
      (`/tara/sit/when-the-mind-wanders?origin=arete-recovery`) — verified
      2026-06-24.
- [x] The destination session writes partial/completion and streak engagement
      records when completed from `?origin=arete-recovery`: the Tara player
      posts to `/v1/tara/sittings/:id/complete`, the BFF creates/reuses the
      member-scoped `Tara recovery return` Arete habit, records a `partial` or
      `done` check-in, and `/v1/arete/room` reads the engaged streak back —
      verified 2026-06-24.
- [x] User chooses the written one-sentence return — the textarea stays disabled
      until the note is long enough, posts `{ note }` through the real BFF,
      creates/reuses the member-scoped `Written recovery return` Arete habit,
      records a `partial` check-in, and `/v1/arete/room` reads the engaged
      streak back — verified 2026-06-26.
- [x] Tara player start from `?origin=arete-recovery` now posts a durable BFF
      start receipt to `/v1/tara/sittings/:id/start`; the BFF records a
      `goal3_stub_tara_session_start` row and dual-writes the canonical
      `v1_tara_ritual_session` `start` event. The browser e2e reads the receipt
      back through `/v1/tara/sittings/:id/sessions` — verified 2026-06-30.
- [ ] Per the
      [Status semantics](../../V1/features.md#arete-habit-goal-and-routine-schema)
      a "decline" is logged with reason and does NOT count, does NOT break
      streak; written/Tara "partial" returns now count as engagement.
- [ ] No catch-up debt is implied; no "make up 3 days" framing

### 6. Plan re-scoping (optional)

If the recovery has been used multiple times, the assistant proactively offers
to lower difficulty or shrink scope per
[`V1/features.md#humane-streak-and-recovery-policy`](../../V1/features.md#humane-streak-and-recovery-policy):

- [x] The coach card on `/arete` ("Two adjustments offered · accept or wait
      until next review") presents the proposed re-scope as an invitation, not a
      prescription — verified 2026-06-24.
- [x] `Approve` / `Not this week` buttons gate the change: browser e2e seeds a
      real habit + check-in, writes the decision through
      `/v1/arete/coach/decision`, and reads it back through `/v1/arete/room` —
      verified 2026-06-24.
- [x] If the user picks `Not this week`, the suggestion defers; the card
      persists the deferral across `/arete` reload, exposes `undo`, and the
      coaching copy does NOT escalate the prompt — verified 2026-06-24.
- [x] If the user approves the offered re-scope, `/arete/plan?intent=adjust`
      reads the same real `/v1/arete/room` decision and carries the approved
      coach adjustment into the plan invitation and next intention row —
      verified 2026-06-26.
- [ ] Per the
      [Friction, Interventions, and Pattern Analysis](../../V1/features.md#friction-interventions-and-pattern-analysis),
      the pattern is captured in the weekly coaching summary, never on
      leaderboards or shared social

### 7. Streak resumes without shaming

- [x] After the user completes an offer, the Arete engagement streak read-back
      updates on `/arete` through `/v1/arete/room` — verified 2026-06-24.
- [ ] `streakNote` rewrites to reflect the quiet days "folded in" — "Two quiet
      days are folded into the count, as we agreed."
- [ ] No "you broke your streak" copy is rendered anywhere — the no-shame floor
      holds across the heatmap, the streak card, the coach card, and the weekly
      review

### 8. Weekly review captures the pattern

Per
[`V1/features.md#weekly-review-and-reflection`](../../V1/features.md#weekly-review-and-reflection):

- [ ] On the next weekly review at `/arete/review`, the structure is "celebrate
      / notice / choose / invite" — never "audit / scold / penalize"
- [ ] The quiet days appear under "notice" with empathy framing, not under
      "celebrate" (false positivity) or anywhere shaming
- [ ] Skipping the weekly review is itself logged but never penalized

## Post-conditions

- The recovery engine persists its `grace_window` or `freeze_window` result;
  completed Tara and written returns persist member-scoped Arete engagement
- The coaching-summary write and the promise that recovery state never reaches
  leaderboards or social surfaces remain requirements until their downstream
  read-back is covered
- Telemetry / persistence trail: `arete_recovery_surface_shown` and
  `arete_recovery_offer_chosen` remain client-side telemetry expectations;
  starting the Tara destination from `origin=arete-recovery` now durably writes
  a Tara ritual-session `start` event through `/v1/tara/sittings/:id/start` and
  reads it back through `/v1/tara/sittings/:id/sessions`; completing that same
  destination durably writes `arete_session_completed` / `arete_session_partial`
  as an Arete check-in (`done` / `partial`) and reads it back on `/arete`.
  Saving the written one-sentence return now durably writes
  `Written recovery return` as an Arete `partial` check-in and reads it back on
  `/arete`.
- Plan re-scope (if accepted) is reflected on `/arete/plan` and on the next
  intention rows

## Failure modes to verify

- [x] **Recovery return writes streak engagement after destination completion**
      — offer cards and `Begin · 6 min` navigate to real return surfaces; the
      recovery page itself stays navigation-only, while the Tara completion POST
      with `origin=arete-recovery` records the Arete `partial` / `done`
      engagement and `/v1/arete/room` reads it back. The same browser flow now
      records the Tara started-event first and reads it back through
      `/v1/tara/sittings/:id/sessions` — verified 2026-06-30.
- [x] **Written recovery note records partial engagement** — the one-sentence
      option is a real textarea + submit control; it posts to
      `/v1/arete/recovery/journal`, records `Written recovery return` as a
      member-scoped `partial` Arete check-in, and `/v1/arete/room` reads it back
      — verified 2026-06-26.
- [x] **Streak surface actions are navigation-only** — `Adjust` and `Begin` on
      `/arete/streak` navigate to `/arete/plan?intent=adjust` and the Tara sit
      player; they do not directly mutate rest-day selection or today's streak
      record — verified 2026-06-26.
- [x] **Approved re-scope propagates into plan surfaces** — `Approve` on
      `/arete` persists through the real BFF, `/arete/plan?intent=adjust` reads
      the approved coach adjustment, and the next intention row carries the same
      label — verified 2026-06-26.
- [ ] **Shaming copy leaks in** — any of: "you broke your streak", "make up X
      days", "your streak is at risk", "behind schedule"; all are hard bans on
      the `coach` persona per
      [`V1/features.md#persona-taxonomy`](../../V1/features.md#persona-taxonomy-tone-band-catalog-and-crisis-recovery-journey)
- [x] **Recovery surface fires too early** — the BFF recovery engine now proves
      the "never before" side of the rule at the boundary: real habits/check-ins
      with two missed windows remain in `grace_window`/`intact`, while the third
      missed window crosses to `freeze_window`/`protected` with the recovery
      action (`arete-streak-recovery`, 2026-06-29). The route-level browser
      surface is still static, so this is trigger-engine coverage rather than a
      routed prompt-gating assertion. The rule is "after 3 quiet days" per
      [`V1/features.md#humane-streak-and-recovery-policy`](../../V1/features.md#humane-streak-and-recovery-policy);
      the streak surface explicitly says "Lilith will check in — never before".
- [ ] **Recovery surface fires during a crisis frame** — per
      [`V1/ARCHITECTURE.md#crisis-frame-cascade`](../../V1/ARCHITECTURE.md#crisis-frame-cascade)
      and the Lilith policy hierarchy, the
      [crisis policy supersedes persona, lineage, pedagogy, and grounding policies](../../V1/features.md#crisis-aware-behavior);
      Arete recovery and humane check-ins are suspended in favor of the safety
      surface (per
      [`crisis-aware-tone-policy.md`](./crisis-aware-tone-policy.md))
- [ ] **`quietDaysLabel` not pluralised** — the fixture passes a plain string
      ("three quiet days"); per the per-view file Open questions, no
      `1 quiet day` vs `3 quiet days` plural-aware handling is visible; verify
      the production string handles 1 / 2 / 3 / 4+ correctly
- [ ] **Empty `data.offers`** — would render the "An offer · not a debt" header
      with no cards; per the per-view file an empty-state design is missing;
      verify behavior
- [ ] **Drift signal leaks to social or leaderboards** — per the no-shame floor
      and the
      [Drift detection](../../V1/features.md#humane-streak-and-recovery-policy)
      rule, drift is flagged only to the user with empathy framing and to the
      coaching summary; never to leaderboards or social — verify the pattern
      doesn't appear on `/activity`, `/messages`, or any shareable surface
- [ ] **Push notification at midnight on day 3** — per the Lilith voice and
      cadence policy, the recovery should NOT push at unsocial hours; user
      quiet-hour settings must be respected (see
      [`shell/05-notifications.md`](../shell/05-notifications.md)); verify the
      `nyx_event_reminder` cadence rules don't accidentally apply to Arete
      recovery
- [ ] **User explicitly declined accountability** — declined accountability
      means no push and no in-app prompt beyond what the recovery surface itself
      provides on visit; the assistant must not initiate

## E2E coverage

- [`apps/oshun/web/e2e/arete-streak-recovery.spec.ts`](../../apps/oshun/web/e2e/arete-streak-recovery.spec.ts)
  — `/arete/recovery` humane soft-return framing (the "What Lilith noticed"
  callout, the "An offer · not a debt" offers, a gentle resumption affordance)
  and the `/arete/streak` "A practice, gently kept." masthead — both asserting
  the no-shame floor (no "broke your streak" / "you failed" / "disappoint" copy)
  — plus the wired streak resumption affordances
  (`Adjust`→`/arete/plan?intent=adjust`,
  `Begin`→`/tara/sit/when-the-mind-wanders`), framing the return as an
  invitation rather than a debt. The spec's new offer-navigation test (step 5)
  asserts all three offer hrefs, then clicks the 6-minute offer and
  `Begin · 6 min` into the real Tara sit route, so the primary offer-selection →
  return-surface navigation is driven instead of asserted as inert. The streak
  test also asserts the 42 heatmap cells expose date + state labels for
  assistive technology. The same spec now seeds a real BFF habit + check-in to
  surface the `/arete` coach card, then verifies `Approve`, `Not this week`, and
  `undo` write through `/v1/arete/coach/decision`, read back through
  `/v1/arete/room`, survive a room reload, and keep the no-shame floor intact.
  The newest recovery-completion test clicks the 6-minute offer into the real
  Tara player, starts the sitting, asserts the live
  `/v1/tara/sittings/when-the-mind-wanders/start` write is accepted, reads the
  start receipt back through `/v1/tara/sittings/when-the-mind-wanders/sessions`,
  scrubs below the 80% threshold, ends the sitting, asserts the live
  `/v1/tara/sittings/when-the-mind-wanders/complete` write is accepted, then
  polls `/v1/arete/room` and renders `/arete` to prove the resulting
  `Tara recovery return` engagement reads back as a 1-day streak with no shaming
  copy. The newest streak-action guard clicks `Adjust` and `Begin` from
  `/arete/streak`, asserts the plan and Tara destinations, verifies the Tara
  player remains `not-started`, and reads `/v1/arete/room` before/after each
  navigation to prove these actions do not mutate Arete engagement state. The
  newest approved-re-scope leg records `Approve` through
  `/v1/arete/coach/decision`, reloads `/arete/plan?intent=adjust`, and asserts
  the plan invitation plus next intention row both carry the approved coach
  action from the real room state. The newest written-return leg fills the
  one-sentence textarea, verifies short-note submit gating, captures the browser
  payload to `/v1/arete/recovery/journal`, forwards it to the live BFF, and then
  reads `/v1/arete/room` plus `/arete` to prove the resulting
  `Written recovery return` `partial` engagement is visible without shaming
  copy. The latest timing test seeds two isolated real member records through
  `/v1/arete/habits` and `/v1/arete/habits/:habitId/check-in`: one with two
  missed windows and one with three. It then reads `/v1/arete/streak` to prove
  the former remains `grace_window` / `intact` and the latter crosses into
  `freeze_window` / `protected`, with exact missed-window labels and humane
  action copy.
- **Coverage depth**: partial — see [`coverage.md`](./coverage.md).
- **Uncovered**: the `/arete/recovery` offers + `Begin · 6 min` are now real
  navigable affordances (faithful `href`s to `/tara/sit` / `/arete/plan`, driven
  by the offer-navigation test), and the written one-sentence return now records
  partial engagement through the live BFF, so step 5 is no longer
  inert/product-blocked. The destination session-completion -> streak-record
  read-back is covered through the live BFF. The BFF trigger-engine timing is
  covered for the two-missed-window grace case and third-missed-window recovery
  threshold. The recovery-origin Tara session-start write is now covered through
  a BFF start receipt and read-back. What remains uncovered is a browser-visible
  route/notification gate that only opens recovery after that engine threshold,
  and the crisis-supersedes-recovery failure mode; cross-domain Tara streak
  consistency lives in the Tara journeys.

## Per-view files touched by this journey

- [`customer/04-arete/arete.md`](../customer/04-arete/arete.md) — Arete hub
- [`customer/04-arete/arete-streak.md`](../customer/04-arete/arete-streak.md) —
  the promise + heatmap
- [`customer/04-arete/arete-recovery.md`](../customer/04-arete/arete-recovery.md)
  — the soft-return surface
- [`customer/04-arete/arete-review.md`](../customer/04-arete/arete-review.md) —
  weekly review with empathy framing
- [`customer/04-arete/arete-plan.md`](../customer/04-arete/arete-plan.md) — plan
  re-scope target
- [`customer/04-arete/arete-coaching.md`](../customer/04-arete/arete-coaching.md)
  — coaching summary card surface
- [`customer/04-arete/arete-patterns.md`](../customer/04-arete/arete-patterns.md)
  — pattern analysis dashboard
- [`customer/02-home-discovery/home.md`](../customer/02-home-discovery/home.md)
  — home rail "Arete practice" section
- [`shell/05-notifications.md`](../shell/05-notifications.md) — quiet hours and
  cadence

## Cross-references

- Feature spec: [`V1/features.md#arete`](../../V1/features.md#arete),
  [`V1/features.md#humane-streak-and-recovery-policy`](../../V1/features.md#humane-streak-and-recovery-policy),
  [`V1/features.md#friction-interventions-and-pattern-analysis`](../../V1/features.md#friction-interventions-and-pattern-analysis),
  [`V1/features.md#weekly-review-and-reflection`](../../V1/features.md#weekly-review-and-reflection),
  [`V1/features.md#lilith-persona-policy`](../../V1/features.md#lilith-persona-policy),
  [`V1/features.md#persona-taxonomy`](../../V1/features.md#persona-taxonomy-tone-band-catalog-and-crisis-recovery-journey)
- Architecture: [`V1/ARCHITECTURE.md`](../../V1/ARCHITECTURE.md) — BFF route
  `/api/oshun/domains/arete`; Lilith policy interlock as the persona / tone gate
- Code:
  - `apps/oshun/web/src/components/lilith/arete.tsx` (`AreteStreak`,
    `AreteRecovery`)
  - `apps/oshun/web/src/components/lilith/rooms.tsx` (`AreteRoom`)
  - `apps/oshun/web/src/lib/lilith-data/arete-depth.ts`
    (`getAreteStreakFixture`, `getAreteRecoveryFixture`)
  - `apps/oshun/web/src/lib/lilith-data/arete.ts` (`getArete`)
- Related journeys:
  - [`crisis-aware-tone-policy.md`](./crisis-aware-tone-policy.md) — how crisis
    policy supersedes the recovery flow
  - [`tara-to-nisaba-handoff.md`](./tara-to-nisaba-handoff.md) — the Tara side
    of "Tara recovery practice" mentioned in the recovery-prompts rule

## Open questions / known gaps

- [x] Wire destination session completion back to Arete so the recovery offer
      records `partial` / `completed` and streak engagement state — verified
      2026-06-24.
- [x] Add a durable session-start write for recovery-origin Tara sittings —
      verified 2026-06-30 via `/v1/tara/sittings/:id/start` +
      `/v1/tara/sittings/:id/sessions`.
- [x] Cover the BFF recovery trigger timing for the two-missed-window grace
      boundary vs the third missed window — verified 2026-06-29 via real
      `/v1/arete/streak`.
- [ ] Add a rest-day picker / streak-record write behind `/arete/streak`'s
      `Adjust` and `Begin` navigation paths, if this surface is meant to mutate
      the streak directly.
- [x] Carry an approved coach card re-scope into `/arete/plan` and the next
      intention rows — verified 2026-06-26.
- [ ] Add plural-aware `quietDaysLabel` rendering (`1 quiet day` vs
      `3 quiet days`) — see per-view file Open questions.
- [ ] Document the exact push cadence and quiet-hours behavior for Arete
      recovery (no notification at midnight, even on day 3).
- [ ] Define the empty-state for the "An offer · not a debt" section when
      `data.offers` is empty.
