# V1 Residual Backlog — 2026-06-11

Successor to `V1_SPEC_GROUND_TRUTH_AUDIT_2026-06-10.md` (all 57 tasks of which
are complete). Built from a fresh 13-agent residual audit of `V1/` against the
post-backlog codebase; the full per-slice evidence reports live in
`docs/audits/V1_RESIDUAL_AUDIT_2026-06-11/` (01–13). ~250 findings: 12 P0-SEC,
~39 P0-HONESTY, ~25 P0-STRUCT, ~87 P1, ~51 P2, ~26 UX.

**Status discipline (unchanged):** every task starts `[ ]`. Mark `[x]` only
after the code is written/verified in-session with the real commit hash
recorded. One task → one verification → one mark → commit + push to BOTH the
branch and main. Each task cites its evidence report section — read it before
starting work.

**2026-09-18 re-scope pass.** Every open item was re-read against its cited
report section and re-checked against the code as it stands today; the paths and
line numbers in the children are today's. The 42 open items were
finding-clusters, so each is now a roll-up line over child checkboxes, one per
independently shippable finding: 224 children, each with a release tier from
`libs/oshun/navigation/src/release-scope.ts` (`[V1.0]` 193, `[V1.1]` 14,
`[V1.2]` 14, `[later]` 3) and a **Verify:** line. 45 dated plain bullets record
findings, or legs of findings, that were found already fixed, with `path:line`
evidence, instead of open boxes; no box was checked by this pass. 21 children
carry a blocker tag (14 wait on another open child, 4 need the owner's
credentials or hardware, 2 are owner decisions, 1 is a human review); the other
203 are actionable. Seven report findings that no backlog item had carried were
attached to the nearest parent and marked "added 2026-09-18": 02 §5 (P17.a, a
bright-line honesty defect), 01 §18 (P22.d), 06 §16 (T3.i), 06 §17 (T2.g), 06
§19 (P11.f), 06 §20 (T2.b) and 06 §21 (T2.h). Two things moved under the reports
since June: `V1/features.md` was split into `V1/features/*.md`, so every
"features.md NNNN" line reference in the reports is stale, and the BFF auth
middleware now lives in `libs/shared/bff-kit/src/authz.ts`. A parent is closed
only when every child under it is closed.

Report key: `01` product-surfaces · `02` tara-arete · `03` veritas-nyx · `04`
nisaba-metis · `05` editorial-studio · `06` assistant-iris-psyche-lilith · `07`
sophia-isis-generation-search · `08` living-scenes · `09` agentic-studio · `10`
admin-tenant · `11` governance-privacy-payments · `12` messaging · `13`
foundations-launch.

---

## Phase S — Security (do first)

- [x] **S1. Tara/Arete identity: spoofable `x-oshun-user-id` → real auth; kill
      shared `house-user`; per-user-scope the global habit/closure/offering
      stores** — _(DONE 2026-06-11, commit `301f490eb7`.)_ Every
      `x-oshun-user-id` read deleted; mutations (sit-complete, habit propose,
      check-in, review close, offering keep) now require the verified bearer
      (`collectionsAuthGuard`); personalized reads (today/room/sittings/
      ritual/streak/habits/review) resolve an OPTIONAL verified member and
      degrade to an honest anonymous baseline. Habits owner-scoped across all
      three store tiers (`owner_user_id` column + `listForOwner`; legacy unowned
      rows served to nobody; check-in 404s on another member's habit; canonical
      dual-write carries the real user). Review closures + offering decisions
      per-member AND durable (snapshot write-through — were volatile global
      maps). Search user-object candidates use `listForOwner`. Web sends the
      bearer on sit-completion + habit-proposal writes. Tests: 87 route/store
      tests green incl. 3 new isolation/401 suites; 24 live-DB tests green incl.
      new cross-owner leg; BFF + web typechecks green; the 1 failing
      TaraSitPlayer test is the pre-existing aria-pressed baseline (verified
      identical stashed). ORIG: every goal3 `/v1/tara/*`+`/v1/arete/*` route
      must resolve the real session user (401 without); per-user-scope tara
      completions/resume/ favorites and arete habits/check-ins/closures/offering
      decisions; add cross-user isolation tests. (02 §1–2)
- [x] **S2. `GET /v1/nyx/observations` unauthenticated + store not user-scoped —
      every member's "private" field log is one global list** — _(DONE
      2026-06-11, commit `9722ac5305`.)_ GET + POST now require the verified
      bearer; `owner_user_id` across all three store tiers with strict
      `listForOwner` (legacy unowned rows served to nobody; snapshot restore
      normalizes); canonical `v1_nyx_logged_observation` dual-write carries the
      real member (was literal `'house-user'`); web form sends the bearer. 88
      route/store tests + 24 live-DB tests green incl. new 401 + cross-member
      isolation legs; BFF + web typechecks green. ORIG: auth-gate all verbs;
      rebuild the store per-user with legacy migration; isolation tests. (03 §4)
- [x] **S3. Metis integrity reads unscoped: any learner can read any verdict
      audit + open appeals on others' verdicts** — _(DONE 2026-06-11, commit
      `90a9c0f394`.)_ Bridge gains `getAcademicIntegrityVerdictLearnerId`; the
      route enforces caller===learner on audit reads + appeal opens and
      party-membership on workspace reads (operator scopes retain review access
      — the GET legs now accept admin:metis too, which they previously 403'd);
      mismatch answers the same 404 as an unknown id (no probing). 5 route tests
      green incl. the new three-leg foreign-learner test; BFF + lib typechecks
      green; 8 themis-wiring lib tests green. ORIG: enforce ownership on audit
      GET + appeal GET/POST via the verdict's subject user; operator scope
      bypass for staff. (04 §1)
- [x] **S4. `/v1/atelier/scenes` + `/v1/lilith-studio/scenes`: global unscoped
      store, unauthenticated read AND write** — _(DONE 2026-06-11, commit
      `002fbb47eb`.)_ Both POSTs auth-gated + `owner_user_id` stamped across all
      three store tiers; the GET is strictly `listForOwner` (anonymous → honest
      empty); the fabricated "For J · 14 May" fixture scenes merged into every
      response are DELETED; canonical dual-writes carry the real member as
      sender/author (were `'house-user'`/`'oshun-studio-author'`); both web
      scene forms send the bearer. 89 route/store + 24 live-DB tests green incl.
      new 401/anonymous-empty/cross-member legs; BFF + web typechecks green.
      ORIG: auth-gate; per-user scope; stamp the real session user on writes.
      (05 §"Atelier/Lilith-studio scenes")
- [x] **S5. Generation job routes unauthenticated: enqueue / drain (fires paid
      provider calls) / read-any-job-by-id** — _(DONE 2026-06-11, commit
      `fd58b47b34`.)_ All three legs now require the SAME Isis workspace scope
      as their siblings (list/retry) — operator-only rather than owner-scoped,
      because the spec's customer path is the curated-card route over the
      in-process enqueue ("raw provider machinery is never customer-facing"); no
      client anywhere polls the raw job-by-id route. New test: 401 anonymous +
      403 wrong-scope on all three; 185 generation tests green; typecheck green.
      ORIG: session-gate enqueue + read; make drain operator-only. (07
      §"Generation job lifecycle routes are unauthenticated")
- [x] **S6. Living Scenes tenant share policy is client-supplied headers — the
      admin `tenantLivingScenesStore` is never enforced server-side** — _(DONE
      2026-06-11, commit `a37ac70bc8`.)_ Share policy (scope/embed), retention
      default/max, the mass-share window, the crisis label (now from the REAL
      crisis-frame runtime), the metis operator-override (now needs a real
      tenant-operator scope), and the `x-oshun-now` clock are ALL resolved
      server-side; the headers survive only behind a non-production test
      override env (`OSHUN_LIVING_SCENES_HEADER_POLICY_OVERRIDES=1`). New tests:
      spoofed allow-headers denied (403 denied_public_scope) for a
      tenant_internal_only tenant, tenant-internal still allowed, untenanted
      default preserved. All living-scenes suites green; typecheck green. ORIG:
      resolve the member's tenant server-side and read policy from the real
      store; ignore the headers on the live path. (08 §6)
- [x] **S7. Agentic executor hardcodes `tier: 'operator-admin'` for
      `output-gallery.retag` — customer-reachable admin mutation** — _(DONE
      2026-06-11, commit `e6c840fa00`.)_ Per-tool scope requirements
      (`toolScopeChecks`) checked against the caller's REAL scopes; retag
      demands the same Isis workspace scope as the dedicated admin route; 403
      `tool_scope_missing` before any execution. (09 F1)
- [x] **S8. Kill-switch `ExecutionTarget` built from client body — family/
      agent/tenant switches evadable by request shaping** — _(DONE 2026-06-11,
      commit `e6c840fa00`.)_ family/provider/region resolve from server metadata
      keyed by tool (`STUDIO_AGENT_TOOL_TARGETS`); body step fields ignored;
      tenant comes only from the verified claim (`'untenanted'` fallback, never
      the body); the clock override is non-production only. NOTE: per-AGENT
      switches still match the caller-declared `rootAgentId` — making those
      airtight needs the runtime agent registry (09 F11, Phase P). 24 runs-route
      tests green incl. the reshaping-evasion leg. (09 F2)
- [x] **S9. `recordExecutedRun` blind upsert by client runId — any user can
      overwrite another user's persisted AgentRun + approval history** — _(DONE
      2026-06-11, commit `e6c840fa00`.)_ The route 409s (`run_id_conflict`) on a
      runId owned by another member BEFORE executing; the store also refuses
      cross-owner overwrites defensively (typed 409 error); owner re-execution
      upsert preserved. New hijack test proves the victim's envelope survives
      intact. (09 F3)
- [x] **S10. `GET /v1/payments/invoices` unauthenticated + unscoped — dumps
      every customer's invoice ledger (purchaserUserId, plan, amounts, txids)**
      — _(DONE 2026-06-11, commit `f705e547ba`.)_ Auth-required; members see
      only their own invoices (anonymous quotes surface only to operators);
      admin:\*/admin:studio retain the full view + tenant filter; the by-id
      route stays a capability URL (random UUID, DTO carries no purchaser). New
      401/cross-member/owner/operator test legs; 84+6 tests green; typecheck
      green. (11 §1)
- [x] **S11. Mobile assistant never reaches the BFF — client-side keyword
      template bypasses Lilith crisis policy, memory, persona, composer** —
      _(DONE 2026-06-11, commit `7870c118b3`.)_ The sheet now creates a real
      assistant session and POSTs every member turn (typed + seeded) to
      `/v1/assistant/sessions/:id/message` with the bearer; crisis interception
      envelopes render the policy statements + real resources inline; the
      keyword model survives ONLY as the explicitly-labeled offline fallback
      ("Offline tips — not a live assistant reply…"). New typed client methods
      (`createAssistantSession`/`sendAssistantMessage`) with bearer/URL tests +
      4 container tests; 18 jest tests green; mobile typecheck green. (06 §6)
- [x] **S12. Unauthenticated, un-rate-limited CPU-bound Living-Scenes render
      endpoint (1920×1920×120 frames)** — _(DONE 2026-06-11, commit `a37ac70bc8`
      with S6.)_ Session-gated; members clamped to 960×960×48 (Isis operators
      keep 1920/120); per-user in-flight cap (1) + 6/min rolling budget
      answering 429. Tests: 401 anonymous, member clamp vs operator envelope,
      budget 429 + per-user isolation. (08 §13)

## Phase H — Honesty (result-faking on live paths)

Work V1.0 children first, then V1.1, then V1.2.

- [ ] **H1. Home surface fabrications** — roll-up of the fabricated-content
      findings on the signed-in Home (daily plan, profile stats, activity strip,
      recommendations, Arete and Nyx fallbacks); children below. (01 §1, §4, §6,
      §7, §16, §17)
  - _Already fixed (checked 2026-09-18): the daily-plan fixture default is gone
    — `apps/oshun/web/src/components/routines/DailyPlanV2.tsx:1070-1077`
    defaults to `[]`, guarded by
    `apps/oshun/web/src/components/home/__tests__/HomeNoFabricatedHistory.spec.tsx`.
    What is left of 01 §1 is H1.a._
  - _Already fixed (checked 2026-09-18): the web profile-stats seed —
    `apps/oshun/web/src/lib/auth-context.tsx:589-600` gives a new member zero
    stats. What is left of 01 §4 is H1.b, H1.c and H1.h._
  - [ ] **H1.a** [V1.0] Feed Home's daily plan from the real routines backend:
        `apps/oshun/web/src/components/home/HomeWorkspace.tsx:558` mounts
        `<DailyPlanV2 />` with no props while `useRoutines()` and
        `useRoutineRecommendations()` in
        `apps/oshun/web/src/lib/hooks/use-bff.ts` have zero callers on Home.
        Pass the hook results as `routines` and `recommendation`; a member with
        no routines keeps the existing empty state; `HomeWorkspace.tsx` imports
        no `SIMULATED_*` export. (01 §1) **Verify:** new
        `apps/oshun/web/src/components/home/__tests__/HomeDailyPlanLive.spec.tsx`
        mocks the BFF boundary — one returned routine renders, an empty list
        renders the empty state, a failed read renders no routine.
  - [ ] **H1.b** [V1.0] Stop defaulting a member to a paid, verified profile:
        `libs/oshun/auth/src/profile-store.ts:257-258` (`toProfileFromSession`)
        and `:279-284` (`createDefaultOshunProfile`) fall back to `plan: 'pro'`,
        `verified: true` and the 7/18/5 demo stats when the session is silent.
        Default to `'free'`, `false` and zeros; keep the demo numbers behind an
        explicit demo-seed argument only. (01 §4) **Verify:** new
        `libs/oshun/auth/src/profile-store-defaults.spec.ts` — a session with no
        plan or verified flag yields free, unverified, zero stats;
        `npx vitest run` on that file from `libs/oshun/auth`.
  - [ ] **H1.c** [V1.0] Give `stats.streakDays` and `stats.activeDomains` a real
        writer or stop rendering them: they are read at
        `apps/oshun/web/src/components/home/HeroBanner.tsx:461,1031` and
        `apps/oshun/web/src/components/ProfileSettingsPanel.tsx:263-265,961-963`
        and nothing ever writes them. Derive the streak from
        `GET /v1/arete/streak` and active domains from the member's real
        activity, or remove both tiles. (01 §4) **Verify:** new
        `apps/oshun/web/src/components/home/__tests__/HeroBannerStreakSource.spec.tsx`
        — the rendered streak equals the mocked `/v1/arete/streak` value and no
        streak line renders when the read fails.
  - [ ] **H1.d** [V1.0] Delete the fabricated Home activity strip:
        `FALLBACK_ACTIVITY` in
        `apps/oshun/web/src/components/home/ActivityFeed.tsx:77` is the initial
        state (`:441`) and the error state (`:477,507,539`). Start empty, and on
        failure render the same unavailable presentation `/activity` uses
        (`apps/oshun/web/src/components/CustomerShellOutageBanner.tsx`) with no
        first-person entries. (01 §6) **Verify:** extend
        `apps/oshun/web/src/components/home/__tests__/ActivityFeed.test.tsx` — a
        500 from `/v1/activity` renders zero activity rows and the outage
        reason; `grep -n FALLBACK_ACTIVITY` returns nothing in the component.
  - [ ] **H1.e** [V1.0] Stop swapping simulated recommendations into Home:
        `apps/oshun/web/src/components/home/MixedDomainRecommendationsLane.tsx:61,112`
        serves `SIMULATED_CROSS_RECOMMENDATIONS` on error or on an empty
        payload, and
        `apps/oshun/web/src/components/cross-domain/CrossDomainRecommendations.tsx:945,972`
        defaults `useDefaultFallback` to `true`. Empty payload renders the
        cold-start invitation, error renders a disclosed unavailable state, and
        the default flips to `false`. (01 §7) **Verify:** extend
        `apps/oshun/web/src/components/home/__tests__/MixedDomainRecommendationsLane.test.tsx`
        — empty and error responses each render zero recommendation cards and no
        `sourceContext` copy.
  - [ ] **H1.f** [V1.0] Remove the fabricated Arete practice fallback:
        `FALLBACK_HOME_PRACTICE` in
        `apps/oshun/web/src/components/home/homeAretePracticeModels.ts:132-219`
        (streaks 12/9/17, progress 68/54/73) renders while
        `/v1/arete/practice/home` is loading or failing. Mirror the Veritas
        module (`homeVeritasBriefingModels.ts`): stage copy plus "practice data
        unavailable", zero numeric claims. (01 §16) **Verify:** extend
        `apps/oshun/web/src/components/home/__tests__/HomeAretePracticeSection.test.tsx`
        — with `practice: null` the section renders no digit followed by `day`
        or `%`.
  - [ ] **H1.g** [V1.0] Remove the fabricated Nyx perspective fallback:
        `FALLBACK_HOME_PERSPECTIVE` in
        `apps/oshun/web/src/components/home/homeNyxPerspectiveModels.ts:96-212`
        asserts "Jupiter at opposition" tonight and a 42% observation in
        progress. The fallback claims no event and no continuation. (01 §17)
        **Verify:** extend
        `apps/oshun/web/src/components/home/__tests__/HomeNyxPerspectiveSection.test.tsx`
        — with `perspective: null` the text `Jupiter` and any percentage are
        absent.
  - [ ] **H1.h** [V1.1] Apply the same defaults on mobile:
        `apps/oshun/mobile/src/auth/mobile-auth-context.tsx:241-243` falls back
        to `plan ?? 'pro'`, `verified ?? true` and passes `stats: undefined`,
        which hands a new member the 7/18/5 demo stats. (01 §4) **Verify:** new
        `apps/oshun/mobile/src/auth/mobile-auth-profile-defaults.spec.tsx`
        (jest, run from `apps/oshun/mobile`) — a fresh session yields free,
        unverified, zero stats.
- [ ] **H2. Billing client-side fiction** — roll-up: rebuild the member billing
      surface on the real persisted plan and invoices (composes with S10);
      children below. (01 §3, §22; 11 §21)
  - _Re-checked 2026-09-18: every finding still holds. The BFF has since gained
    a Stripe fiat rail (`apps/oshun/bff/src/payments/fiat-routes.ts`),
    `GET /v1/billing/subscription-management`
    (`apps/oshun/bff/src/routes/billing-subscription-management.ts:105`) and the
    member-scoped `GET /v1/payments/invoices`
    (`apps/oshun/bff/src/routes/domain-stubs.ts:3762`); the web surface reads
    none of them._
  - [ ] **H2.a** [V1.0] Seed billing honestly empty:
        `createDefaultOshunBillingSnapshot` in
        `libs/oshun/auth/src/billing-store.ts:979-1025` seeds
        `currentPlan: 'pro'`, "Visa ending in 4242" and a paid 1200-cent invoice
        for every member. Seed `free`, no payment methods, no invoices. (01 §3)
        **Verify:** new `libs/oshun/auth/src/billing-store-seed.spec.ts` — the
        default snapshot has plan `free`, zero methods, zero invoices.
  - [ ] **H2.b** [V1.0] Give the web billing store a BFF fetcher like its
        siblings: `apps/oshun/web/src/profile/store.ts:68-71` builds it on
        `localStorage` only. Hydrate plan and renewal from
        `GET /v1/billing/subscription-management` and invoices from
        `GET /v1/payments/invoices`; an unreachable BFF renders a disclosed
        unavailable state, never the seed. (01 §3) **Verify:** new
        `apps/oshun/web/src/profile/billing-store-hydration.spec.ts` — a mocked
        free member renders free with no invoices; a mocked settled invoice
        renders once; a 503 renders the unavailable state.
  - [ ] **H2.c** [V1.0] Route "purchase" into a real payment flow:
        `handlePurchaseClick` in
        `apps/oshun/web/src/components/profile/BillingSection.tsx:128-142`
        mutates `localStorage`, announces "active · renews" and calls
        `syncProfilePlan` (`:1524`), which flips the client plan that
        `apps/oshun/web/src/components/billing/ShellFeatureGate.tsx:46-47`
        reads. Send the member to the crypto quote (`/billing/crypto`) or the
        Stripe checkout the fiat rail exposes, or show an honest
        `not_configured` state; the client never writes its own plan. (01 §3)
        **Verify:** extend
        `apps/oshun/web/src/components/profile/__tests__/BillingSection.test.tsx`
        — clicking purchase performs a navigation or BFF call and leaves the
        profile plan unchanged until the server reports it.
  - [ ] **H2.d** [V1.0] Remove the fabricated backup card:
        `addBackupPaymentMethod` in
        `apps/oshun/web/src/components/profile/BillingSection.tsx:248-262`
        invents a Visa 4242 or Mastercard 4444 with an expiry three years out.
        Remove the control, or hand off to the payment provider's own
        method-collection flow. (found while re-checking 01 §3) **Verify:** same
        spec as H2.c — no code path calls `upsertPaymentMethod` with a literal
        `last4`.
  - [ ] **H2.e** [V1.0] Replace "Report failed charge": the button at
        `apps/oshun/web/src/components/profile/BillingSection.tsx:689` calls
        `simulateBillingFailure` (`:295`), which injects a declined-card state
        into the member's own billing. Post a billing-category report to
        `POST /v1/feedback` (`apps/oshun/bff/src/routes/feedback.ts`) instead
        and delete `simulateBillingFailure`. (01 §22) **Verify:** same spec as
        H2.c — clicking the button sends one `/v1/feedback` request and the
        billing snapshot is unchanged.
  - [ ] **H2.f** [V1.0] Build the member billing view:
        `apps/oshun/web/src/app/billing/page.tsx` is a static link card. Render
        the session plan, the member's invoice history and a receipt link per
        settled invoice (the by-id invoice DTO already carries the signed
        receipt, `apps/oshun/bff/src/routes/domain-stubs.ts:3788-3795`). (11
        §21) **Verify:** new `apps/oshun/web/e2e/billing-history.spec.ts`
        (chromium, `workers=1`) — a member with one settled invoice sees it with
        a receipt link; a free member sees an honest empty history.
  - [ ] **H2.g** [V1.1] Apply H2.a and H2.b on mobile:
        `apps/oshun/mobile/src/profile/store.ts:41` uses the same seed and
        `apps/oshun/mobile/app/(tabs)/profile.tsx:1233` renders its invoice. (01
        §3) **Verify:** new
        `apps/oshun/mobile/src/profile/billing-store-hydration.spec.ts` (jest) —
        a fresh member renders free with no invoice.
- [x] **H3. Web notifications center + `/v1/notifications` fabricate seven
      canonical notifications (fake billing alarm, fake safety review, fake
      data-export-ready) whenever the live feed is empty** — _(DONE 2026-06-11,
      commit `5e0040e295`.)_ `buildCanonicalLaneNotifications` DELETED from the
      BFF (items now derive only from the real domain feed;
      admin/support/privacy/safety lanes honestly 0 until their stores are
      wired); the web panel's 13-item fabricated fallback DELETED — failed feed
      → disclosed outage notice, empty feed → honest empty state; old fixture
      content moved to the mocked network boundary in tests. NOTE:
      reminder-inbox wiring deferred to P4 (InAppMessage carries no domain —
      threading it through the dispatcher belongs with the inbox-UI task). 9
      BFF + 44 web tests green; typechecks green. (01 §5; 12 §2)
- [ ] **H4. Mobile fabrications sweep** — roll-up of the fabricated content on
      the native tabs; every child is V1.1 because the native apps ship in V1.1;
      children below. (01 §8–13)
  - _Already fixed (checked 2026-09-18): the capture "permission granted"
    fiction — `apps/oshun/mobile/app/(tabs)/index.tsx:1219-1237` now calls
    `requestMobileCapturePermission`, which asks the OS through
    `apps/oshun/mobile/src/capture/mobileCapturePermissionsBridge.ts:68,88`.
    What is left of 01 §13 is H4.g._
  - [ ] **H4.a** [V1.1] Activity tab: delete `INITIAL_NOTIFICATIONS`
        (`apps/oshun/mobile/app/(tabs)/activity.tsx:95`, initial state at
        `:522,531`) and stop `syncNotifications` returning early without a dev
        token (`:744`). Start empty and loading; signed out shows a sign-in
        prompt; a failed read shows an unavailable state with no timeline. (01
        §8) **Verify:** new
        `apps/oshun/mobile/app/(tabs)/activity-honest-states.spec.tsx` (jest) —
        signed-out, failed and empty reads each render zero notification rows.
  - [ ] **H4.b** [V1.1] Profile tab memory controls: replace
        `INITIAL_MOBILE_PROFILE_MEMORY_ENTRIES`
        (`apps/oshun/mobile/app/(tabs)/profile.tsx:405`, state at `:707,713`)
        with hydration from `GET /v1/iris/adapter/review` and
        `/v1/iris/adapter/consents`, the reads the web page uses
        (`apps/oshun/web/src/app/profile/memory/memory-state.ts:258-259`);
        forget, pause and export call the same BFF routes as web. Closes 06 §21
        for the read side. (01 §9) **Verify:** extend
        `apps/oshun/mobile/app/(tabs)/profile.test.tsx` — with an empty review
        response no entry mentioning `Renata` renders and consent shows
        ungranted.
  - [ ] **H4.c** [V1.1] Profile tab linked sessions: `INITIAL_LINKED_SESSIONS`
        (`apps/oshun/mobile/app/(tabs)/profile.tsx:292`) is the initial state
        (`:700`) and the signed-out state (`:970`). Start empty; signed out
        shows a sign-in prompt; an error shows the error. (01 §9) **Verify:**
        same spec file as H4.b — a signed-out render lists zero devices and the
        governance row reads zero sessions.
  - [ ] **H4.d** [V1.1] Library tab Iris handoff:
        `apps/oshun/mobile/app/(tabs)/library.tsx:36` calls
        `buildMobileHomeIrisHandoffState()` with no arguments, which renders the
        fixture in `apps/oshun/mobile/src/navigation/mobileIrisHandoff.ts:30,84`
        (memory counts, "64% progress"). Lift the home tab's
        `/v1/iris/mobile-handoff` fetch into a shared hook and render "no
        handoff waiting" when it is null. (01 §10) **Verify:** extend
        `apps/oshun/mobile/src/navigation/mobileIrisHandoff.test.ts` (jest) — a
        null handoff renders the empty card; the builder has no default fixture
        path.
  - [ ] **H4.e** [V1.1] Universal-share card:
        `apps/oshun/mobile/app/(tabs)/library.tsx:48` calls
        `buildMobileUniversalShareModel()` with no requests, so
        `apps/oshun/mobile/src/share/mobileUniversalSharing.ts:47,63-130`
        invents an active link and an invite to `learner@example.edu`. Feed it
        from the real library-sharing reads
        (`apps/oshun/bff/src/routes/library-sharing.ts`) or render the empty
        share center; delete the default requests. (01 §11) **Verify:** extend
        `apps/oshun/mobile/src/share/mobileUniversalSharing.test.ts` — no
        requests yields zero links and zero invites.
  - [ ] **H4.f** [V1.1] Calendar-sync card:
        `apps/oshun/mobile/app/(tabs)/index.tsx:911` builds state from
        `buildDefaultMobileCalendarSyncItems()` and the static transport
        (`apps/oshun/mobile/src/calendar/mobileCalendarSync.ts:164,358,588`),
        and "Sync now" fires a success haptic without calling anything. Drive
        the card from the BFF calendar-connection routes the web uses
        (`apps/oshun/web/src/profile/calendar-connections-client.ts`);
        unconnected shows "Connect a calendar"; "Sync now" calls the real sync
        route or is removed. (01 §12) **Verify:** extend
        `apps/oshun/mobile/src/calendar/mobileCalendarSync.test.ts` (jest) —
        with no connections the state lists zero providers and the static
        transport is unreachable from the default path.
  - [ ] **H4.g** [V1.1] Capture card: permission is now real, but a ready
        capture action only routes to an ordinary screen
        (`apps/oshun/mobile/app/(tabs)/index.tsx:1240-1275`); no camera,
        microphone or photo capture exists. Build a minimal capture-to-upload
        leg on `expo-camera` and `expo-image-picker` (both in
        `apps/oshun/mobile/package.json`) over the avatar asset-store seam, or
        remove the capture actions from Home and keep the redaction-policy
        model. (01 §13) **Verify:** extend
        `apps/oshun/mobile/src/capture/mobileCaptureIntake.test.ts` — a ready
        action either produces an upload request or the card is absent; no
        action is labelled capture while only navigating.
- [x] **H5. `/arete` hub is the last fixture room + all 8 `/arete/*` depth pages
      serve fabricated intimate narratives** — _(DONE 2026-06-12, commit
      `559bad49b5`.)_ New `GET /v1/arete/room` over the member's REAL
      habits/check-ins (trailing-week completion, aggregated week stats,
      humane-engine coach note, honestly-empty journal line); `getArete()`
      fetches it with an honest `areteUnavailable()` fallback; AreteRoom
      discloses empty states. The 8 fixture depth pages carry the preview
      register via `app/arete/layout.tsx` (hub/habits/offerings explicitly
      unlabeled). Full depth-page WIRING (streak/review pages → the real C2
      endpoints) remains P17. 87 BFF + 14 web tests green; typechecks green. (02
      §3–4)
- [x] **H6. The check-in loop has no UI: C2's POST has zero callers; the only
      check-in UI is orphaned AND fakes its save** — _(DONE 2026-06-12, commit
      `f80147378e`.)_ New `AreteHabitCheckInControls`
      (done/partial/skip/decline) on every `/arete/habits` row POSTing the real
      C2 endpoint with the bearer, rendering only the SERVER's recorded
      streak/recovery outcome; home "Check in" launch repointed at
      `/arete/habits` (was the coach chat); the orphaned fake-save
      DailyCheckInOverlay deleted with its tests. NOTE: the sit-player
      reflection fake-save sub-item is 02 §5 (H5's hub cluster /P17) — still
      open there. 2 new + 7 facade + 24 overlay tests green; typechecks green.
      (02 §6)
- [x] **H7. `/domains/*` fake-result tools: AreteAICoach canned replies + fake
      typing; TaraSoundLibrary play/mixer/binaural UI with zero audio code** —
      _(DONE 2026-06-12, commit `8c435e26f6`.)_ Coach chat starts empty and
      routes every turn through the real `/v1/arete/coach/responses` composer
      (unreachable → disclosed, never simulated). Sound library plays REAL
      audio: ambient layers stream per-sound deterministic BFF WAVs; binaural =
      real stereo OscillatorNodes at catalog frequencies; bells = struck-bell
      additive synthesis; music catalog (no recordings in V1) discloses on play.
      The fabricated COACH_SESSIONS history remains in the banner-registered
      session-list views (labeled example data). 953 domain tests green;
      typecheck green. (02 §7–8)
- [x] **H8. D1 banner hole: the preview banner mounts only in the `[domainId]`
      catch-all — 54+ standalone `/domains/*` static sub-routes
      (veritas/nyx/tara/arete/nisaba tools) bypass the register entirely** —
      _(DONE 2026-06-11, commit `f3ed8125a4`.)_ New `app/domains/layout.tsx`
      mounts a path-resolved `DomainPreviewBannerForPath` for the WHOLE segment
      (all 55 deep pages + future ones); the duplicate room-level mount removed;
      `DOMAIN_PREVIEW_SURFACES` stays the single switch. 7 new tests over the
      spec-named deep pages; 81 domain-surface tests green; typecheck green. (01
      §14; 02 §9; 03 §3)
- [x] **H9. Veritas
      `/veritas/{claim,source,story,topic,counterclaims,mobile,     provenance,retraction}`
      serve fabricated journalism unlabeled — and the real hub deep-links into
      them ("Read sources" → invented Norway-grid claim)** — _(DONE 2026-06-12,
      commit `1783477729`.)_ New `app/veritas/layout.tsx` mounts a path-resolved
      preview register on every depth sub-route (the real hub stays unlabeled;
      same DOMAIN_PREVIEW_SURFACES switch); the real lead's "Read sources" now
      anchors to the on-page evidence rail rendering the article's REAL
      claims/sources. 12 tests green; pre-existing lilith baselines verified
      stashed; typecheck green. (03 §2)
- [x] **H10. `/v1/activity` fabricates Nisaba/Metis timeline items for every
      member; `/v1/metis/room` fabricates streak/minutes/assessment/tutor nudge
      unguarded in prod** — _(DONE 2026-06-12, commit `ba6e01061a`.)_ Activity
      nisaba branch now reads the member's REAL notebooks; metis branch reads
      REAL tutor sessions + ingested outlines (new
      `metisIngestJobStore.listForOwner`), honest-empty otherwise, no fabricated
      grounded/verified badges or dead hrefs. Metis room: honest zero-state
      weekStats/resume/assessment; curated catalog progress 0; tutor nudge from
      the REAL durable tutor-session store or honest absence. NOTE for H11:
      nisaba activity now reflects the store, so the seeded fixture notebooks
      surface until H11 removes the seed. ALSO surfaced: `courseProposals` store
      is globally shared (any member's drafts shown to all as "drafted by you")
      — owner-scoping needed, tracked under Q1/P18 follow-up. 9+5+89 BFF + 19
      web tests green; typechecks green. (04 §2–3)
- [x] **H11. Nisaba per-user seed fabricates member-authored annotations/
      notebooks + "verified" grounded-evidence views with invented citations on
      unbannered consumer routes; C7 search ranks them as the member's real
      objects** — _(DONE 2026-06-12, commit `e20f45cfaf`.)_ All three seed
      families DELETED (3 member-authored annotations, 2 pre-built notebooks, 3
      grounded/verified evidence views with invented citations + misquoted MN
      118); members start honestly empty; passage annotation counts start 0 and
      stay real; grounded evidence 404s until the real pipeline runs; curated
      passages/labeled witness/compare lanes remain (dangling refs emptied).
      NOTE: the `/v1/nisaba/room` "third today" sub-item is 04 §6 (P1) and stays
      tracked under P18. 10+10+26 BFF + 48 web tests green; typecheck green. (04
      §4; 05 §1)
- [x] **H12. `/studio/authoring`: unlabeled fixture doc, fabricated
      Sophia/Lilith/Isis governance bindings, "autosave: saved" with zero
      persistence — also an orphan route** — _(DONE 2026-06-12, commit
      `c705918aac`.)_ Explicit rehearsal register rendered (seeded example /
      in-memory only / illustrative bindings); autosave says "held in-memory
      (not persisted)"; readiness + AI-assist copy no longer claim live bindings
      or an upstream agent; route linked from the studio hub as "(rehearsal)".
      Real persisted artifact store + live bindings remain P19/T-scope. 6 tests
      green; typecheck green. (05 §authoring)
- [ ] **H13. Assistant fabrications** — roll-up of the fabricated assistant,
      memory-control and safety surfaces; children below. (06 §4–§9)
  - _Already fixed (checked 2026-09-18): forget-a-fact, pause and export reach
    the server — `apps/oshun/web/src/app/profile/memory/memory-state.ts:418-424`
    (`DELETE /v1/iris/adapter/memory/:id`), `:376-380` (`PATCH …/pause`),
    `:431-432` (`POST …/export`)._
  - _Already fixed (checked 2026-09-18): the fabricated "Renata" crisis record —
    web reads `GET /v1/safety/crisis-frames/mine`
    (`apps/oshun/web/src/app/profile/safety/ProfileSafetyJourney.tsx:526,605`);
    mobile builds from the real frame
    (`apps/oshun/mobile/src/safety/MobileProfileSafetyJourney.tsx:57-66,151`)._
  - _Already fixed (checked 2026-09-18): the faked tool-progress bars and the
    four invented sessions —
    `apps/oshun/web/src/components/assistant/AssistantPanel.tsx` has no
    simulated progress, and a failed history read sets `historyUnavailable`
    (`:4629-4634`, rendered at `:7343-7345`)._
  - _Already fixed (checked 2026-09-18): the "Calculus I 34%" adapters —
    `apps/oshun/bff/src/routes/assistant.ts:442-475` delegates Nisaba and Metis
    to `app.domainAdapters` like the other rooms._
  - [ ] **H13.a** [V1.0] Send the remaining memory clears to the server:
        category clear, scope clear, scope opt-out and the full clear with its
        seven-day grace in
        `apps/oshun/web/src/app/profile/memory/ProfileMemoryControls.tsx:606-690`
        still only `commit()` a local view-model. Add bulk-delete and full-clear
        legs to the Iris adapter routes (`apps/oshun/bff/src/routes/iris.ts`),
        call them, then re-hydrate. (06 §4) **Verify:** new
        `apps/oshun/bff/src/routes/iris-memory-bulk-clear.spec.ts` — after a
        category clear the review read returns no entry of that category; a web
        spec asserts the toast appears only after a 2xx.
  - [ ] **H13.b** [V1.0] Replace the step-up theatre: the challenge is the
        constant `'iris-step-up'` compared in the browser
        (`apps/oshun/web/src/app/profile/memory/ProfileMemoryControls.tsx:78,407`).
        Require the real session step-up the BFF already uses for device
        sessions (`apps/oshun/bff/src/routes/auth-device-sessions.ts`) on the
        destructive Iris routes and drop the client-side phrase. (06 §4)
        **Verify:** extend the H13.a BFF spec — a destructive call without a
        fresh step-up answers 403 `step_up_required`.
- [x] **H14. Sophia operator evidence queue (`AdminEvidenceQueuePanel`) is a
      100% hardcoded fixture presented as live operator data; customer gallery
      provenance inspector renders canned bundles instead of calling the live C6
      provenance route** — _(DONE 2026-06-12, commit `5e935fbf06`.)_ Evidence
      queue carries an explicit rendered `seedDataNotice` (real wiring of the
      verification loops = P7); the gallery inspector now fetches the REAL C6
      provenance for the SELECTED output with honest
      idle/loading/not-recorded/not-authorized/unreachable states — the canned
      Saraswati bundles are gone from the live surface (the fixture component's
      three remaining operator mounts are Q-register work). 14 gallery + 3
      evidence tests green; typecheck green. (07 §2–3)
- [x] **H15. Living Scene customer card fakes Keep/Share/generation success with
      zero network calls; /studio/compose is fixture theater** — _(DONE
      2026-06-12, commit `506edff5a6` — per the fix sketch's fallback.)_ All
      card actions now report honestly (delegated neutrally when a parent wires
      a callback; "not wired on this surface — nothing happened" otherwise; cue
      evaluation stays real and says so); compose (web + mobile) opens with an
      explicit rehearsal register over its constant segment library/assist/0.92
      pre-score. REAL keep/share wiring needs a session-produced score — follows
      the conductor runtime (T14) + share surface cluster (P9). 28 web + 1
      mobile tests green; typechecks green. (08 §1–2)
- [x] **H16. Admin honesty: bulk-operation "staged commit" stamps a timestamp
      and applies nothing; integrations registry boots unlabeled fixture
      keys/webhooks/connectors with fabricated "healthy" probes** — _(DONE
      2026-06-12, commit `2678dfc6c3`.)_ Commit now DISPATCHES every non-skip
      plan row to per-kind executors over the real stores ('users' →
      session-less `provisionAccount` + tenant seat; 'entitlements' →
      persisted-plan flip); unwired kinds fail LOUD; row failures fail the op
      while recording honest partial `commitOutcomes` (new contract field).
      Integrations registry: production boots EMPTY; dev seeds carry an
      auto-dropping `seedDataNotice`; the dead `PlaceholderDataBanner` is now
      mounted over it. 47 store/route tests + 21 contracts specs green;
      typechecks green. Rosters/content/taxonomy/persona_assignments executors
      remain fail-loud pending their store seams (P-phase). (10 §4, §6)
- [x] **H17. T&S operator inbox interleaves real customer reports with unlabeled
      fabricated cases including a fake CSAM/NCMEC record** — _(DONE 2026-06-12,
      commit `5a4a79b4ec`.)_ Every served user report now carries
      `origin: 'live' | 'seed'` (stamped at the workspace read, added to the
      contract schema); the admin console renders "seeded example — not a
      customer report" on seed rows; the seeded image-moderation case
      fabricating a verified NCMEC CSAM hash match is replaced with a non-CSAM
      graphic-violence example, with a test asserting NO seeded case may ever
      carry csam/ncmec signals. 7 admin + 6 BFF + 50 contracts tests green;
      typechecks green. (11 §6)

## Phase T — Structural (spec pillars unreachable)

Work V1.0 children first, then V1.1, then V1.2.

- [ ] **T1. `/v1/oshun/*` facades for Veritas and Nyx** — roll-up; the
      structural gap is closed by a different architecture than the one this
      item named, and two Veritas hardening findings remain; children below. (03
      §1; 01 §15)
  - _Already fixed (checked 2026-09-18): Nyx, Nisaba and Metis no longer dial a
    downstream service — `apps/oshun/bff/src/app.ts:1779-1802` binds in-process
    adapters by default (`apps/oshun/bff/src/nyx/in-process-adapter.ts` over the
    `@oshun/domain-nyx` ephemeris and the durable
    `apps/oshun/bff/src/nyx/nyx-member-stores.ts`), so the 18 Nyx operations
    answer without a facade._
  - _Already fixed (checked 2026-09-18): the Veritas facade exists — 17
    `/v1/oshun/*` routes in
    `apps/veritas/api/src/interfaces/http/routes/v1/oshun-facade.ts:247-752`,
    mounted at `apps/veritas/api/src/interfaces/http/server.ts:186`, over the
    same Postgres tables as the native routes._
  - [ ] **T1.a** [V1.2] Gate the Veritas facade with the service token:
        `apps/veritas/api/src/interfaces/http/routes/v1/oshun-facade.ts` takes
        the member id from the query (`:251,329,372,585`) or the body
        (`:642,664`) and checks no credential, while the Arete facade fails
        closed (`facadeAccessCheck`,
        `apps/arete/api/src/routes/oshun-facade.ts:180-195`). Require
        `x-oshun-service-token` to equal `VERITAS_OSHUN_FACADE_TOKEN`, refuse in
        production when the variable is unset, and keep development open. (03
        §1) **Verify:** new
        `apps/veritas/api/src/interfaces/http/routes/v1/oshun-facade-token.spec.ts`
        — wrong token 401, production with no token configured 401, matching
        token 200.
  - [ ] **T1.b** [V1.2] Add the Veritas contract round-trip test the Tara and
        Arete facades have
        (`apps/oshun/bff/src/adapters/tara-oshun-facade-contract.test.ts`,
        `arete-oshun-facade-contract.test.ts`): boot the real facade router and
        drive every Veritas method of `createDomainServiceAdapters` through it.
        (03 §1) **Verify:** new
        `apps/oshun/bff/src/adapters/veritas-oshun-facade-contract.spec.ts` —
        all 17 operations parse through `normalizeDomainServicePayload` with no
        drift.
- [ ] **T2. Iris memory loop** — roll-up; bootstrap and per-turn recall are now
      wired, so what remains is the consent door, the unused recall and
      governance engines, and three findings of report 06 that no backlog item
      carried; children below. (06 §1–3; added 2026-09-18: 06 §17, §20, §21)
  - _Already fixed (checked 2026-09-18): the bridge is bootstrapped and closed
    per session — `apps/oshun/bff/src/routes/assistant.ts:2094`
    (`bootstrapMemory`) and `:5621` (`closeMemory`)._
  - _Already fixed (checked 2026-09-18): recall is consulted per turn —
    `apps/oshun/bff/src/routes/assistant.ts:796` calls
    `memoryBridge.recallRelevantMemory`._
  - _Already fixed (checked 2026-09-18, 06 §18): the Iris export builds a real
    bundle (`apps/oshun/bff/src/adapters/iris-memory-adapters.ts:1010`) and the
    DSAR export includes memory
    (`apps/oshun/bff/src/data-export/bundle-builder.ts:82`)._
  - [ ] **T2.a** [V1.0] Build the consent door: `POST /v1/iris/adapter/consents`
        (`apps/oshun/bff/src/routes/iris.ts:564`) has no client;
        `apps/oshun/web/src/app/profile/memory/memory-state.ts:259` only GETs.
        Add grant and deny controls per category on `/profile/memory` that POST
        with `role=settings` and re-hydrate, so durable memory becomes
        reachable. (06 §3) **Verify:** new
        `apps/oshun/web/e2e/profile-memory-consent.spec.ts` (chromium,
        `workers=1`) — grant a category, reload, the category reads granted from
        the server; deny reverses it.
  - [ ] **T2.b** [V1.0] Make the Iris consent store the one source of truth for
        the assistant: session create still derives `memoryConsentGranted` from
        profile preferences (`apps/oshun/bff/src/routes/assistant.ts:2082`).
        Read it from the Iris adapter, and have the assistant-controls toggle
        POST the same consent route as T2.a. (06 §20) **Verify:** new
        `apps/oshun/bff/src/routes/assistant-memory-consent.spec.ts` — a member
        with a preference flag but no Iris grant gets a session with memory off.
  - [ ] **T2.c** [V1.0] Apply the privacy-aware recall filter:
        `filterIrisPrivacyAwareRecall`
        (`libs/oshun/memory-iris/src/privacy-suppression.ts`) and
        `RecallPipeline` (`libs/oshun/memory-iris/src/recall/pipeline.ts`) have
        zero consumers in `apps/`. Route `GET /v1/iris/adapter/search`
        (`apps/oshun/bff/src/routes/iris.ts:650`) and the assistant recall at
        `assistant.ts:796` through them so suppressed and sensitive categories
        never reach a turn. (06 §2) **Verify:** new
        `apps/oshun/bff/src/routes/iris-recall-suppression.spec.ts` — a fact in
        a suppressed category is stored, then absent from both the search route
        and the assistant recall summary.
  - [ ] **T2.d** [V1.0] Surface write conflicts instead of overwriting: wire
        `resolveIrisMemoryWriteConflict`
        (`libs/oshun/memory-iris/src/conflict-resolution.ts`) into the adapter's
        remember path in `apps/oshun/bff/src/adapters/iris-memory-adapters.ts`.
        (06 §2) **Verify:** new
        `apps/oshun/bff/src/adapters/iris-memory-conflict.spec.ts` — two
        contradicting facts produce a recorded conflict with both versions
        retrievable.
  - [ ] **T2.e** [V1.0] Mount `InspectionStateMachine`
        (`libs/oshun/memory-iris/src/admin-inspection/state-machine.ts`) behind
        the `role=admin` reads of `apps/oshun/bff/src/routes/iris.ts` so an
        operator inspection needs a reason, a time box and an audit event. (06
        §2) **Verify:** new
        `apps/oshun/bff/src/routes/admin-iris-inspection.spec.ts` — a read
        without an open inspection is refused; an expired inspection is refused;
        each granted read writes one audit event.
  - [ ] **T2.f** [V1.0] Census the remaining unused memory engines and decide
        each in code: `InferenceEngine` (`inference/policy.ts`),
        `ActorHandleNamespace` (`multi-actor/namespace.ts`) and
        `assessIrisProfileMemoryDecay` (`retention-compaction.ts`) under
        `libs/oshun/memory-iris/src/`. For each either wire one real caller in
        the BFF or mark the module `@deprecated` with a dated note naming the
        release it waits for. (06 §2) **Verify:** `grep -rn` for each symbol
        over `apps/` returns a caller, or the module header carries the dated
        deferral; no third state.
  - [ ] **T2.g** [V1.0] Emit and read continuation tokens: the routes in
        `apps/oshun/bff/src/routes/iris.ts` (write, read, resume, notices over
        `@oshun/memory-iris/continuity/protocol`) have zero web or mobile
        callers. Write a checkpoint from the Tara sit player and the Nisaba
        reader, and render `GET /v1/iris/continuation-tokens` as Home
        continuation cards. (06 §17) **Verify:** new
        `apps/oshun/web/e2e/continuation-token-resume.spec.ts` — pause a sit,
        open Home in a second context, the continuation card resumes at the
        saved position.
  - [ ] **T2.h** [V1.1] Mobile memory controls: `apps/oshun/mobile` has only the
        indicator `src/components/AssistantMemoryState.tsx` and no screen that
        lists, forgets or consents. Build it over the same routes as web
        (composes with H4.b, which removes the fixture). (06 §21) **Verify:**
        new `apps/oshun/mobile/src/memory/MobileMemoryControls.spec.tsx` (jest)
        — forget calls `DELETE /v1/iris/adapter/memory/:id`; consent calls the
        POST of T2.a.
- [ ] **T3. Persona governance** — roll-up: one canonical persona id space,
      policy packs and lifecycle bound to it, tone bands enforced, and one
      persona-surface finding of report 06 that no backlog item carried;
      children below. (06 §10–11; added 2026-09-18: 06 §16)
  - _Re-checked 2026-09-18: all five id universes are still disjoint and the
    tone-band functions still have no caller outside
    `libs/oshun/persona-policy-lilith`._
  - _Already fixed (checked 2026-09-18, 06 §15): `/profile/persona` and its
    route agree — `GET /v1/persona/voice`
    (`apps/oshun/bff/src/routes/personas-consumer.ts:144-185`) serves
    `providerId`, `voiceKind`, `defaultLocale` and a null `previewUrl`, which is
    what `apps/oshun/web/src/app/profile/persona/page.tsx` renders._
  - [ ] **T3.a** [V1.0] Key Lilith policy packs by launch-roster ids:
        `apps/oshun/bff/src/adapters/lilith-persona-policy-adapters.ts:37,159,257`
        knows only `tara-contemplative-guide` and `veritas-stoic-scholar`, so
        `checkPersonaTopic('lotus-mindful', …)` finds no pack. Give every
        persona in `libs/oshun/persona-registry/src/launch-roster.ts` a pack.
        (06 §10) **Verify:** new
        `apps/oshun/bff/src/adapters/lilith-persona-policy-roster.spec.ts` —
        iterates the roster and asserts a pack resolves for every id.
  - [ ] **T3.b** [V1.0] Key the admin lifecycle store by roster ids and make it
        bite: `apps/oshun/bff/src/admin/persona-lifecycle-store.ts:83-84`
        governs `persona-zen-guide` and `persona-stoic-guide`, which no member
        can select. Re-key to the roster, and make a retired or deprecated
        persona disappear from `apps/oshun/bff/src/routes/personas-consumer.ts`
        browse and select. (06 §10) **Verify:** extend
        `apps/oshun/bff/src/routes/personas-consumer.test.ts` — retire a roster
        persona through the admin store, browse omits it and select answers 409.
  - [ ] **T3.c** [V1.0] Replace the panel's private persona catalog:
        `apps/oshun/web/src/design-system/assistant-persona-switching.ts:9-145`
        (`oshun-navigator`, `zen-guide`, `stoic-scholar`, …) is persisted
        client-side and the panel never calls `/v1/personas/select`. Browse and
        select through the server, and drop the `personaHandoff` body field the
        BFF never reads. (06 §10) **Verify:** extend
        `apps/oshun/web/src/components/assistant/__tests__/AssistantPanel.test.tsx`
        (or a new `.spec.tsx` beside it) — switching persona issues one
        `/v1/personas/select` call and the transcript label is the server's.
  - [ ] **T3.d** [V1.0] Give the Psyche runtime a roster default:
        `apps/oshun/bff/src/adapters/psyche-embodiment-adapters.ts:288,328,339`
        hardcodes "Guide One". Resolve the member's selected roster persona, or
        the roster default. (06 §10) **Verify:** new
        `apps/oshun/bff/src/adapters/psyche-persona-default.spec.ts` — the
        session persona id is a member of the launch roster.
  - [ ] **T3.e** [V1.0] Enforce tone bands on generation: call
        `enforceIsisGenerationDispatchTone`
        (`libs/oshun/persona-policy-lilith/src/tone-bands/enforcement.ts`) in
        the Isis admit path (`apps/oshun/bff/src/generation/jobs-route.ts`),
        deriving the active band from surface and persona role with
        `resolveEffectiveToneBand`. (06 §11) **Verify:** new
        `apps/oshun/bff/src/generation/tone-band-admit.spec.ts` — a
        `contemplative-strict` request above the band's audacity cap is refused
        with a typed reason.
  - [ ] **T3.f** [V1.0] Enforce tone bands on sharing: call
        `enforceShareControlTone` in
        `apps/oshun/bff/src/routes/library-sharing.ts` and the Living Scenes
        share create in `apps/oshun/bff/src/routes/living-scenes.ts`. (06 §11)
        **Verify:** new `apps/oshun/bff/src/routes/share-tone-band.spec.ts` — a
        band that caps share reach to private refuses a public link.
  - [ ] **T3.g** [V1.0] Enforce tone bands in the Living Scenes conductor with
        `enforceLivingScenesConductorTone`. 2026-09-18: there is no conductor
        runtime to call it from until T14.c is decided. (06 §11) **Verify:** a
        conductor-path spec asserts the band's motion cap clamps an accepted
        cue. `blocked:upstream`
  - [ ] **T3.h** [V1.0] Enforce lineage policy at composition time:
        `checkPublishLineage` and `emitLineageBinding`
        (`libs/oshun/persona-policy-lilith/src/sacred-symbols/lineage-binding.ts`)
        have no caller. Call them where a composed scene or offering is kept
        (`apps/oshun/bff/src/routes/living-scenes.ts` keep route). (06 §11)
        **Verify:** new
        `apps/oshun/bff/src/routes/living-scenes-lineage.spec.ts` — a
        composition using a lineage-bound symbol without its binding is refused.
  - [ ] **T3.i** [V1.0] Make the selected voice pack change the voice:
        `speakText` builds a bare `SpeechSynthesisUtterance`
        (`apps/oshun/web/src/components/assistant/AssistantPanel.tsx:4369`) and
        never assigns a voice, rate or pitch. Map each roster persona to a
        `speechSynthesis.getVoices()` profile as the V1.0 binding; the hosted
        TTS provider stays a deploy-time setting. (06 §16) **Verify:** new
        `apps/oshun/web/src/components/assistant/__tests__/assistant-voice-binding.spec.ts`
        — two personas produce utterances with different `voice` or `rate`.
- [ ] **T4. Agentic approval→execution disconnect** — roll-up; the executor was
      rebuilt as a governed orchestrator, so transcripts and in-run budget
      debits are done and the approval queue and the server budget registry
      remain; children below. (09 F4–F6)
  - _Already fixed (checked 2026-09-18): executed envelopes now carry
    transcripts —
    `libs/oshun/agentic-studio/src/runs/governed-orchestrator.ts:578-903`
    appends every call to `toolCalls`, and
    `apps/oshun/bff/src/agentic/runs-route.ts:662-664` persists the run with its
    audit events._
  - _Already fixed (checked 2026-09-18): meters are debited within a run —
    `governed-orchestrator.ts:269` threads `consumeBudget`._
  - [ ] **T4.a** [V1.0] Connect approval to execution: `approve()` moves a run
        to `queued` (`apps/oshun/bff/src/agentic/run-lifecycle-store.ts:730`)
        and nothing consumes queued runs, while the execute route answers 409
        `run_already_active` for any stored non-terminal run
        (`apps/oshun/bff/src/agentic/runs-route.ts:497-503`), so an approved run
        can never execute and "Run now" ends at "Released to Lilith's composer"
        (`apps/oshun/web/src/components/lilith/AreteReviewDraftRun.tsx:54-55`).
        Let execute accept a run whose lifecycle status is `queued`, refuse a
        gated run that is not, and stamp the result back onto the lifecycle
        record. (09 F4) **Verify:** extend
        `apps/oshun/bff/src/agentic/runs-route.test.ts` — submit, approve,
        execute reaches `completed`; executing a gated run that is still
        `awaiting_approval` answers 409.
  - [ ] **T4.b** [V1.0] Bind a server budget registry: `resolveBudgets` is an
        optional hook (`apps/oshun/bff/src/agentic/runs-route.ts:404,648`) that
        `apps/oshun/bff/src/server.ts:3807` never passes, and the route still
        merges `body.budgets` (`:649`). Add a Postgres-backed per-tenant,
        per-agent, per-tool registry, pass it as `resolveBudgets`, persist
        meters across runs, and stop accepting client envelopes in production.
        (09 F5) **Verify:** extend
        `apps/oshun/bff/src/agentic/runs-route.test.ts` — a tenant cap of N
        units refuses the run that would cross it across two separate requests,
        with no `budgets` in either body.
- [ ] **T5. API keys accepted on real requests** — roll-up; a key can now
      authenticate, but only against a probe route and with no rate limit;
      children below. (10 §1; 13 F8)
  - _Already fixed (checked 2026-09-18): a request can present a key —
    `GET /v1/integrations/auth-probe`
    (`apps/oshun/bff/src/routes/admin-integrations-registry.ts:46-110,625-629`)
    calls `store.authenticateApiKey` and enforces tenant, scope, IP allowlist
    and expiry, and last-used now moves._
  - [ ] **T5.a** [V1.0] Extract the probe's key check into a reusable preHandler
        in `libs/shared/bff-kit/src/` (`createApiKeyAuthPreHandler`, mapping key
        scopes onto `request.authContext`) and mount it on the first real
        partner-facing route group; the SCIM routes of P16.a are the intended
        first consumer. Note `apps/oshun/bff/src/middleware/authz.ts` is now a
        re-export shim; the middleware lives in
        `libs/shared/bff-kit/src/authz.ts`. (10 §1) **Verify:** new
        `libs/shared/bff-kit/src/api-key-auth.spec.ts` — missing key 401, wrong
        scope 403, revoked key 401, valid key populates `authContext.tenantId`.
  - [ ] **T5.b** [V1.0] Enforce the stored rate limit: `evaluateApiKeyRateLimit`
        (`libs/oshun/tenant-console/src/integrations/api-keys.ts:85`) has no
        caller, so `rateLimitPerMinute` is configuration nothing reads. Call it
        in the preHandler of T5.a with the window kept in the existing
        abuse-protection store and answer 429 with `retry-after`. (10 §1)
        **Verify:** same spec as T5.a — request N+1 inside a minute answers 429
        with a positive `retry-after`.
- [ ] **T6. Outbound webhooks** — roll-up: the delivery executor exists and
      nothing constructs an event or keeps a secret across a restart; children
      below. (10 §2, §11; 13 F7)
  - _Re-checked 2026-09-18: `executeWebhookDelivery` and
    `createWebhookDeliveryExecutor` still have no caller under
    `apps/oshun/bff/src`._
  - [ ] **T6.a** [V1.0] Wire a `fetch`-based `WebhookHttpTransport` and
        `createWebhookDeliveryExecutor`
        (`libs/oshun/tenant-console/src/integrations/webhook-delivery-execution.ts`)
        in the BFF composition root, with the retry loop on an interval timer
        like the reminder worker and dead-letter rows readable by the admin
        panel. (10 §2) **Verify:** new
        `apps/oshun/bff/src/admin/webhook-delivery-runtime.spec.ts` — a local
        HTTP listener receives one signed POST whose signature verifies; a 500
        response is retried on the backoff schedule and then dead-lettered.
  - [ ] **T6.b** [V1.0] Persist signing secrets: they live in an in-memory `Map`
        (`apps/oshun/bff/src/admin/admin-integrations-registry-store.ts:168,351,398,515`)
        excluded from the persisted state, so every pre-restart subscription
        fails `SIGNING_SECRET_UNAVAILABLE`. Store them wrapped with AES-GCM
        under an env-provided key in the durable state; with no key configured
        the store refuses to create a subscription rather than keep a plaintext
        copy. (10 §2, §11) **Verify:** new
        `apps/oshun/bff/src/admin/webhook-secret-durability.spec.ts` — export
        state, import into a fresh store, the simulator signs successfully; the
        persisted JSON contains no raw secret.
  - [ ] **T6.c** [V1.0] Publish real events: construct a `WebhookEvent` and call
        `subscriptionsForEvent` from the admin audit choke point
        (`adminAuditEventsStore.record`) for takedown, moderation decision,
        generation release and payment settlement. (10 §2) **Verify:** extend
        the T6.a spec — settling a test invoice delivers one `payment.settled`
        event to a subscribed listener and none to an unsubscribed topic.
- [ ] **T7. Roles and tenant auth policy feed live authorization** — roll-up:
      the roles engine and the auth-challenge engine each have one demo-page
      consumer and the running system authorizes on literal scope strings;
      children below. (10 §3, §8)
  - _Re-checked 2026-09-18: `effectiveCapabilities`, `runDryRun` and
    `evaluateAuthChallenge` are still consumed only by
    `apps/oshun/tenant-admin/src/app/roles/page.tsx` and
    `apps/oshun/tenant-admin/src/app/identity/page.tsx:115`; the live check is
    `scopes.includes(...)` in `libs/shared/bff-kit/src/authz.ts:145`._
  - [ ] **T7.a** [V1.0] Add a Postgres-backed role-template and role-assignment
        store with `/v1/admin/role-templates` CRUD over
        `libs/oshun/tenant-console/src/roles/roles.ts`
        (`validateTemplateWithinApprovedBounds`, `transitionAssignment`). (10
        §3) **Verify:** new
        `apps/oshun/bff/src/routes/admin-role-templates.spec.ts` — a template
        outside approved bounds is refused; an assignment expires on its date;
        rows survive a store re-open.
  - [ ] **T7.b** [V1.0] Resolve roles into scopes at session mint: when a token
        is issued for a tenant member, expand their assignments with
        `effectiveCapabilities` into the scope set, so editing a template
        changes what the next session can do. (10 §3) **Verify:** new
        `apps/oshun/bff/src/auth/role-scope-expansion.spec.ts` — removing a
        capability from a template removes the matching scope from the next
        minted token and the route answers 403.
  - [ ] **T7.c** [V1.0] Expose the dry-run harness: a route over `runDryRun`
        that reports what a proposed template change would grant or revoke for
        named members, without applying it. (10 §3) **Verify:** same spec file
        as T7.a — the dry-run response lists the revoked capability and the
        store is unchanged.
  - [ ] **T7.d** [V1.0] Add `tenantAuthPolicyStore` (MFA requirement, step-up
        triggers, IP allowlist, session refresh) and evaluate it with
        `evaluateAuthChallenge`
        (`libs/oshun/tenant-console/src/identity/federation-and-auth-policy.ts:76`)
        in the SSO callback (`apps/oshun/bff/src/auth/sso-login-routes.ts`) and
        in `createAuthPreHandler` for step-up routes, answering 403
        `step_up_required`. (10 §8) **Verify:** new
        `apps/oshun/bff/src/auth/tenant-auth-policy.spec.ts` — a login from
        outside the tenant's allowlist is refused; a step-up route without a
        fresh factor answers `step_up_required`.
- [ ] **T8. Billing and support runtime** — roll-up; the billing library is no
      longer orphaned (the Stripe rail uses it), and dunning, crypto renewal,
      support cases and the trust-and-safety engines remain; children below. (11
      §2–5)
  - _Already fixed (checked 2026-09-18): `@oshun/billing-support` has runtime
    consumers — the Stripe fiat rail and plan projection
    (`apps/oshun/bff/src/payments/fiat-routes.ts`, `fiat-stripe-store.ts`,
    `apps/oshun/bff/src/billing-support/plan-sync.ts`, `billing-store.ts`) and
    the trial, family, cancel-flow and scholar-verification routes. Stripe
    subscriptions carry real periods (`fiat-routes.ts:186-290`)._
  - _2026-09-18: this item was written before V1/TODOS.md §23.2 added Stripe as
    a second rail; T8.b is the crypto-rail half only._
  - [ ] **T8.a** [V1.0] Wire dunning: `recordPaymentFailure`,
        `advanceDunningStage` and `degradedEntitlementOnLapse`
        (`libs/oshun/billing-support/src/dunning/dunning.ts:83,115,155`) have no
        app caller. Drive them from the Stripe `invoice.payment_failed` and
        subscription-lapse events in
        `apps/oshun/bff/src/payments/fiat-routes.ts`, and degrade the plan
        through `apps/oshun/bff/src/billing-support/plan-sync.ts` when the grace
        window ends. (11 §2) **Verify:** extend
        `apps/oshun/bff/src/payments/fiat-routes.test.ts` — a failed-payment
        event opens a grace window, a later success clears it, expiry projects
        the plan to `free`.
  - [ ] **T8.b** [V1.0] Give the crypto rail a billing period: settlement still
        calls `updatePlanDurably` with no expiry
        (`apps/oshun/bff/src/payments/settlement-route.ts:302`), so one settled
        invoice is a lifetime plan. Stamp a period end at settlement, add a
        renewal worker that issues the next invoice through
        `apps/oshun/bff/src/payments/quote-builder.ts`, feed the offsets of
        `libs/oshun/payments-bridge/src/customer-surface/reminder-cadence.ts`
        into the reminder dispatcher, and apply the same lapse path as T8.a. (11
        §3) **Verify:** new
        `apps/oshun/bff/src/payments/crypto-renewal-worker.spec.ts` — with a
        fixed clock, a plan past its period end with no settled renewal projects
        to `free`; a settled renewal extends the period.
  - [ ] **T8.c** [V1.0] Build support-case intake: no `/v1/support*` route
        exists and the operator workspace triages seeded cases. Add
        `POST /v1/support/cases` (session-stamped) over a Postgres store, assign
        queue and SLA with `routeCase` and `slaForCase`
        (`libs/oshun/billing-support/src/support-cases/support-cases.ts:88,122`),
        add an admin queue read and decide pair, and replace the `mailto:` on
        `apps/oshun/web/src/app/billing/page.tsx` with the intake. (11 §4)
        **Verify:** new `apps/oshun/bff/src/routes/support-cases.spec.ts` — a
        member's case lands in the routed queue with an SLA deadline, another
        member cannot read it, `evaluateCaseSla` flags it after the deadline.
  - [ ] **T8.d** [V1.0] Stamp severity and SLA on moderation work: call
        `classifySeverity` and `slaFor` from `@oshun/trust-safety` when an item
        enters `apps/oshun/bff/src/moderation/moderation-store.ts` or the
        abuse-report store, and expose the breach state on the admin read. (11
        §5) **Verify:** new
        `apps/oshun/bff/src/moderation/moderation-severity.spec.ts` — a
        CSAM-class report is P0 with the P0 triage deadline; an overdue item
        reads breached.
  - [ ] **T8.e** [V1.0] Run the repeat-offender ladder: call `applyOffense` on
        every upheld decision and persist the per-user counter in Postgres; the
        resulting restriction is what P12.a enforces. (11 §5) **Verify:** same
        spec file as T8.d — three upheld decisions move a user from warn to
        restrict to suspend.
  - [ ] **T8.f** [V1.0] Add `GET /v1/admin/safety/dashboard` over
        `buildSafetyDashboard`, fed from the real moderation and abuse-report
        stores. (11 §5) **Verify:** new
        `apps/oshun/bff/src/routes/admin-safety-dashboard.spec.ts` — counts
        equal the rows in the stores; an empty deployment reads zeros.
- [ ] **T9. Telegram bot runway** — roll-up; the code is done and only the
      owner's live registration is left; children below. (12 §1)
  - _2026-09-18: every code finding re-checked and found fixed — the BFF webhook
    delivers through `deliverTelegramResponses`
    (`apps/oshun/bff/src/routes/telegram.ts:3,317-319`); `@oshun/telegram-bot`
    is declared a library with no entrypoint
    (`.github/workflows/deploy-hetzner.yml:21-23`,
    `infra/hetzner/README.md:43-45`); both deployment planes are held in step by
    `apps/oshun/bff/src/telegram/deployment-parity.spec.ts`; webhook
    registration is scripted in `infra/hetzner/scripts/telegram-webhook.sh`.
    Verify and close once T9.a is done._
  - _Owner decision 2026-09-18: Telegram is Eve's first live channel._
  - [ ] **T9.a** [V1.0] Go live: the owner creates the bot with BotFather, puts
        `OSHUN_TELEGRAM_BOT_TOKEN` and `OSHUN_TELEGRAM_WEBHOOK_SECRET` in the
        Hetzner stack `.env`, and runs
        `infra/hetzner/scripts/telegram-webhook.sh register staging`.
        2026-09-18: the bot token is the owner's credential; no agent can mint
        one. (12 §1, §12) **Verify:** `telegram-webhook.sh status staging` shows
        the staging URL with no last error, and a `/start` sent to the bot is
        answered in the chat. `blocked:external`
- [ ] **T10. Analytics loop** — roll-up: the shared transport is real and the
      live telemetry modules still do not use it; children below. (13 F1–F2)
  - _Re-checked 2026-09-18: `createSharedAnalyticsSinks` is used only by
    `taraAnalytics.ts`, `studioWorkspaceMountTelemetry.ts` and `transport.ts`
    under `apps/oshun/web/src/analytics/`; `initOshunAnalytics`
    (`libs/oshun/analytics/src/singleton.ts:19`) has no caller; eleven modules
    still carry the "Temporary local sink" comment._
  - [ ] **T10.a** [V1.0] Initialise the singleton at shell boot: call
        `initOshunAnalytics({ context, sinks: createSharedAnalyticsSinks() })`
        once in `apps/oshun/web/src/lib/providers.tsx`, beside
        `ExperimentationBootstrap`. (13 F2) **Verify:** new
        `apps/oshun/web/src/analytics/analytics-singleton-boot.spec.tsx` — after
        the provider mounts, `getOshunAnalytics().track(...)` reaches the mocked
        `POST /v1/analytics/events`.
  - [ ] **T10.b** [V1.0] Flip the live console-sink modules to the shared
        transport: `publicAuthFunnelTelemetry`, `recommendationTelemetry`,
        `pwaLifecycleTelemetry`, `shellNavigationTelemetry`,
        `searchResultTelemetry`, `homeContinuationTelemetry`,
        `domainLaunchTelemetry`, `librarySaveTelemetry`,
        `activityReentryTelemetry`, `notificationCenterTelemetry` and
        `nisabaStudyTelemetry` in `apps/oshun/web/src/analytics/`. The sink must
        honour the analytics consent of P13.d. (13 F1, F16) **Verify:**
        `grep -rln "Temporary local sink" apps/oshun/web/src/analytics` prints
        nothing; one spec per module family asserts an event reaches the mocked
        ingest.
  - [ ] **T10.c** [V1.0] Decide `taraAnalytics.ts`: it is wired to the real
        transport and has no importer. Call `useTaraAnalytics()` from the sit
        player and the Tara onboarding, or delete the module and its tests. (13
        F1) **Verify:**
        `grep -rn taraAnalytics apps/oshun/web/src --include=*.tsx` shows a
        component importer, or the file is gone.
- [ ] **T11. Metis adaptive-learning spine** — roll-up; Metis ships in V1.2, so
      every child is V1.2; children below. (04 §5)
  - _Partly fixed (checked 2026-09-18): `@metis/gradebook` now has a runtime
    consumer — `apps/oshun/bff/src/metis/gradebook-store.ts`
    (`GradebookEmitter`), used by
    `apps/oshun/bff/src/routes/metis-integrity.ts:21`. `@metis/assessment`,
    `@metis/learning` and `@metis/tutoring` still have no consumer under
    `apps/oshun` or `apps/metis`._
  - [ ] **T11.a** [V1.2] Drive the adaptive loop from the tutor-memory route:
        feed each turn of `POST /v1/metis/tutor-session-memories`
        (`apps/oshun/bff/src/routes/metis-tutor-memory.ts`) into
        `libs/metis/tutoring/src/loop/adaptive-loop.ts` with IRT item selection
        from `@metis/assessment`, and return the next item. (04 §5) **Verify:**
        new `apps/oshun/bff/src/routes/metis-adaptive-loop.spec.ts` — a run of
        correct answers raises the selected item difficulty; the P95 of the
        selection step is under 600 ms on the dev server.
  - [ ] **T11.b** [V1.2] Persist mastery transitions: record every band change
        from `libs/metis/learning/src/mastery/transitions.ts` in Postgres with
        the evidence that caused it. (04 §5) **Verify:** same spec file as T11.a
        — a transition row exists after the band changes and none when it does
        not.
  - [ ] **T11.c** [V1.2] Emit gradebook entries on assignment completion through
        the file-export and xAPI adapters of `@metis/gradebook`; LTI-AGS and
        Caliper stay behind their `not_configured` seams until an LMS endpoint
        is provided. (04 §5) **Verify:** extend the specs beside
        `apps/oshun/bff/src/metis/gradebook-store.ts` — completing an assignment
        yields one entry and one xAPI statement; re-completion is idempotent.
  - [ ] **T11.d** [V1.2] Bring-your-own-model safety harness: add a tenant
        model-endpoint registry and put
        `libs/shared/inbound-integrations/src/byom.ts` on the call path with a
        per-endpoint kill switch. (04 §5) **Verify:** new
        `apps/oshun/bff/src/metis/byom-model-harness.spec.ts` — an endpoint that
        fails the sandbox checks is never called; an armed kill switch refuses
        the next call.
- [ ] **T12. Tara and Arete data planes** — roll-up: one system of record per
      fact, the facade being the architecture chosen in C1; children below. (02
      §10–11)
  - _Re-checked 2026-09-18: both planes still serve the same facts. S1 closed
    the identity half of 02 §11._
  - [ ] **T12.a** [V1.0] Tara progress: a sit completion is written to the goal3
        store (`POST /v1/tara/sittings/:id/complete`,
        `apps/oshun/bff/src/routes/domain-stubs.ts:1634`, read by
        `/v1/tara/analytics` at `:1699`) while
        `/v1/tara/{streak,history,continue}` read the facade
        (`apps/oshun/bff/src/routes/tara.ts:175-250`). Forward the completion to
        the facade's progress write and keep goal3 as a write-through cache, or
        read both from one store. (02 §10) **Verify:** new
        `apps/oshun/bff/src/routes/tara-single-plane.spec.ts` — completing one
        sitting changes `/v1/tara/history`, `/v1/tara/streak` and
        `/v1/tara/analytics` together.
  - [ ] **T12.b** [V1.0] Arete streak: `/v1/arete/streak` computes from goal3
        check-ins (`apps/oshun/bff/src/routes/domain-stubs.ts:1731`) and the
        reminder worker computes at-risk streaks from the facade
        (`apps/oshun/bff/src/server.ts:3483`). Make both read one source. (02
        §10) **Verify:** new
        `apps/oshun/bff/src/routes/arete-single-streak.spec.ts` — after a
        check-in the route and the worker's producer report the same streak.
  - [ ] **T12.c** [V1.0] Habit completions: two write paths, the check-in at
        `apps/oshun/bff/src/routes/domain-stubs.ts:1928` and
        `logHabitCompletion` at `apps/oshun/bff/src/routes/routines.ts:76`.
        Converge them and record the boundary in each route header. (02 §10)
        **Verify:** same spec file as T12.b — a completion logged through the
        routines route counts in `/v1/arete/streak`.
- [ ] **T13. Collections** — roll-up: one durable collection system with a share
      link that shows something; children below. (05 §collections/share; 01 §21
      read-back)
  - _Already fixed (checked 2026-09-18): share policies and links are durable —
    `wireDurableLibrarySharePolicies`
    (`apps/oshun/bff/src/library/share-policy-store.ts:524-529`, wired at
    `apps/oshun/bff/src/server.ts:1903`)._
  - _Already fixed (checked 2026-09-18): the link routes somewhere —
    `apps/oshun/web/src/app/share/[token]/page.tsx` exists and calls the resolve
    endpoint (`:100`). What it can show is T13.b._
  - [ ] **T13.a** [V1.0] Back the dashboard's collections with the server:
        `apps/oshun/web/src/components/library/LibraryDashboard.tsx:114-180`
        ships four fixture collections and keeps all collection CRUD in
        `localStorage` (`oshun.library.collections.v1`), never calling
        `/v1/library/collections`. Reuse the saved-items sync pattern of
        `apps/oshun/web/src/lib/library/webLibraryStore.ts`; drop the fixtures
        or gate them to signed-out guests. (05 §collections) **Verify:** new
        `apps/oshun/web/e2e/library-collections-one-system.spec.ts` — a
        collection created on `/library/collections/new` appears in the
        dashboard manager, and one created in the dashboard appears on
        `/library/collections` after reload.
  - [ ] **T13.b** [V1.0] Make a granted share carry content:
        `GET /v1/library/shared/:collectionId` returns only
        `{collectionId, granted, tier, via}`
        (`apps/oshun/bff/src/routes/library-sharing.ts:231-264`). On grant
        include the collection label and its items, filtered by the granted
        tier. (05 §share) **Verify:** new
        `apps/oshun/bff/src/routes/library-sharing-content.spec.ts` — a
        view-tier token returns label and items; a revoked token returns
        neither.
  - [ ] **T13.c** [V1.0] Check ownership before the first policy:
        `setVisibility` and `createShareLink` make the first caller the owner of
        any `collectionId`
        (`apps/oshun/bff/src/library/share-policy-store.ts:232-244`) without
        consulting the collections store. Refuse unless the caller owns the
        collection. (05 §share) **Verify:** same spec file as T13.b — a second
        member cannot create a policy for the first member's collection id.
- [ ] **T14. Living Scenes rendering and conductor** — roll-up: score-driven
      rendering first, honest cue handling, and an owner decision on conductor
      scope; children below. (08 §3–4)
  - _Re-checked 2026-09-18: all findings still hold._
  - [ ] **T14.a** [V1.0] Render from the score: the share path calls
        `renderLivingSceneSegmentToPng({ tone: 'contemplative', … })` with
        nothing taken from `artifact.score`
        (`apps/oshun/bff/src/routes/living-scenes.ts:1380-1385`), so every scene
        is the same gradient. Derive per-segment tone, frame count from
        `durationSeconds` and phase from `perSegmentSeedsHex`, and concatenate
        the segment runs into the APNG. (08 §3) **Verify:** new
        `apps/oshun/bff/src/routes/living-scenes-score-render.spec.ts` — two
        different scores produce different media hashes; the same score renders
        byte-identical twice.
  - [ ] **T14.b** [V1.0] Make cue handling honest and safe: the accepted-cue
        notice says "cue applied at the next scene boundary"
        (`libs/yemaya/living-scenes-runtime/src/customer-card/customer-card.ts:407`)
        although nothing applies it, and `decideCrisisAction`
        (`libs/yemaya/living-scenes-runtime/src/cues/crisis-classifier.ts`) is
        never run. Say "cue accepted", and classify every voice or text cue
        before acceptance. (08 §4) **Verify:** extend the customer-card specs in
        `libs/yemaya/living-scenes-runtime/src/customer-card/` — the notice no
        longer says applied; a crisis-class cue is refused with the crisis
        action.
  - [ ] **T14.c** [V1.0] Decide conductor scope for V1.0: minimal cue-to-segment
        application, or defer the streaming conductor
        (`libs/yemaya/living-scenes-runtime/src/conductor/conductor.ts`, zero
        callers) to a later release. 2026-09-18: the report recommends deferral;
        the owner has not decided. (08 §4) **Verify:** the decision is recorded
        in `V1/TODOS.md` with its date, and T3.g is re-scoped to match.
        `blocked:governance`
- [ ] **T15. Consent ledger customer surface** — roll-up: the real ledger has no
      door, and voice and avatar consent cannot be granted in the product;
      children below. (11 §9)
  - _Re-checked 2026-09-18: `apps/oshun/web/src/app/profile/data/page.tsx:57`
    still reads the fixture route `/v1/data-rights/consent`, and nothing under
    `apps/oshun/web/src` calls `/v1/consent`._
  - [ ] **T15.a** [V1.0] Point `/profile/data` at the real ledger: read
        `GET /v1/consent` (`apps/oshun/bff/src/routes/consent.ts`) and add grant
        and withdraw controls per flow that post to the per-flow verbs. (11 §9)
        **Verify:** new `apps/oshun/web/e2e/profile-consent-ledger.spec.ts` —
        withdraw the analytics flow, reload, it reads withdrawn; the audit read
        lists the transition.
  - [ ] **T15.b** [V1.0] Mount a first-use consent prompt on the persona voice
        and avatar picker, since
        `apps/oshun/bff/src/routes/personas-consumer.ts` already refuses those
        modalities without a granted flow. (11 §9) **Verify:** extend
        `apps/oshun/bff/src/routes/personas-consumer.test.ts` and add a web spec
        — selecting a voice persona without consent shows the prompt; granting
        it makes the select succeed.
  - [ ] **T15.c** [V1.0] Delete the fixture route `/v1/data-rights/consent` from
        `apps/oshun/bff/src/routes/domain-stubs.ts` once T15.a lands.
        2026-09-18: depends on T15.a. (11 §9) **Verify:**
        `grep -rn "data-rights/consent" apps/oshun` returns nothing.
        `blocked:upstream`

## Phase P — P1 wiring register (work after S/H/T; full detail in reports)

Work V1.0 children first, then V1.1, then V1.2.

- [ ] **P1. Channel-binding verification codes** — roll-up: the bind route never
      sends the code, so no verified binding can exist and every email, SMS and
      WhatsApp reminder is suppressed; children below. (12 §3)
  - _Re-checked 2026-09-18:
    `apps/oshun/bff/src/routes/channel-bindings.ts:133-142` still reports
    `missing-config` in production and `preview` elsewhere without attempting a
    send. This is the same defect as the verification-code item of
    `docs/audits/V1_REMAINING_WORK_IMPLEMENTATION_CHECKLIST_2026-06-08.md` (its
    §2.1 note near line 925); close both together._
  - [ ] **P1.a** [V1.0] Send the email code: in
        `apps/oshun/bff/src/routes/channel-bindings.ts` dispatch the minted code
        through `deliverWithEnvProviders`
        (`libs/oshun/messaging-channels/src/provider-config-env.ts:231`), the
        path the signup verification already uses
        (`apps/oshun/bff/src/auth/verification-email-sender.ts`). Report the
        transport's real outcome; keep `missing-config` only when the
        environment has no email provider. (12 §3) **Verify:** new
        `apps/oshun/bff/src/routes/channel-bindings-delivery.spec.ts` with the
        SMTP provider pointed at local Mailpit (`:1025`, inbox API on `:8025`) —
        binding an address puts one message containing the code in Mailpit, and
        verifying with it yields `status: 'verified'`.
  - [ ] **P1.b** [V1.0] Send the SMS and WhatsApp codes through the same call,
        with a transport double at the provider boundary in tests; with no
        Twilio or Meta configuration the route answers `missing-config` and
        stores nothing it would claim as sent. (12 §3) **Verify:** same spec
        file as P1.a — the double receives one send per bind; with the env
        cleared the response is `missing-config` and the double is never called.
  - [ ] **P1.c** [V1.0] Prove one live SMS and one live WhatsApp verification.
        2026-09-18: needs the owner's Twilio and Meta Cloud credentials in
        `~/.config/oshun/env-master.env`; none exist there. (12 §12) **Verify:**
        a real handset receives the code and the binding reads verified.
        `blocked:external`
- [ ] **P2. Notification preferences the dispatcher never reads** — roll-up:
      quiet hours, master push, per-domain and per-kind toggles are stored and
      not enforced, and the planner defaults to opted-in; children below. (12
      §4–5)
  - _Re-checked 2026-09-18: all three findings still hold._
  - [ ] **P2.a** [V1.0] Make one store authoritative for quiet hours: Telegram
        `/quiet` writes `notificationPreferencesStore`
        (`apps/oshun/bff/src/telegram/effects-adapter.ts:60-66`) and the
        preferences PATCH writes the same place, but the dispatcher and the
        preferences read both overwrite `enabled` with the consumer-profile flag
        (`apps/oshun/bff/src/reminders/reminders-route.ts:668-671`,
        `apps/oshun/bff/src/routes/notifications-preferences.ts:121-123,305`).
        Pick one, and have every writer and reader use it. (12 §4) **Verify:**
        extend `apps/oshun/bff/src/reminders/reminders-route.test.ts` — after a
        Telegram `/quiet` a reminder inside the window is suppressed; after the
        PATCH with `enabled: false` it is delivered.
  - [ ] **P2.b** [V1.0] Enforce the send-side toggles:
        `enrichReminderWithLivePolicy`
        (`apps/oshun/bff/src/reminders/reminders-route.ts:662-692`) reads quiet
        hours, bindings and the crisis frame and nothing else. Drop `userOptIns`
        to empty when the member's master `pushEnabled` is off or the reminder's
        domain has `modalities.push` or `kinds.reminder` off
        (`apps/oshun/bff/src/notifications/preferences-store.ts`). (12 §5)
        **Verify:** same test file — with push off a push reminder is suppressed
        and an in-app one is still delivered.
  - [ ] **P2.c** [V1.0] Default external channels to off: `toChannelSet` in
        `libs/oshun/messaging-channels/src/v3-session-reminders.ts:308-313`
        treats a missing `userOptIns` as opted in. For every channel except
        in-app, absence means suppress. (12 §5) **Verify:** extend
        `libs/oshun/messaging-channels/src/v3-session-reminders.test.ts` — a
        plan with no opt-ins dispatches in-app only.
- [ ] **P3. WhatsApp and FCM delivery legs** — roll-up; message content now
      reaches WhatsApp, and the template registry, status ingestion and the FCM
      token remain; children below. (12 §6–7)
  - _Already fixed (checked 2026-09-18): WhatsApp sends carry the body —
    `libs/oshun/messaging-channels/src/transports.ts:358-366` adds template
    `components`, fed by `delivery.ts:533`._
  - [ ] **P3.a** [V1.0] Route WhatsApp sends through
        `planWhatsAppTemplateDelivery`
        (`libs/oshun/messaging-channels/src/whatsapp/index.ts`): per-locale and
        per-use-case template approval, the 24-hour session window and the
        cost-ledger entry, replacing the single global
        `OSHUN_WHATSAPP_TEMPLATE`. (12 §6) **Verify:** extend
        `libs/oshun/messaging-channels/src/whatsapp/whatsapp.test.ts` and
        `delivery.test.ts` — an unapproved locale is refused before any HTTP
        call; an open session window selects the session path.
  - [ ] **P3.b** [V1.0] Ingest WhatsApp delivery statuses: add
        `POST /v1/webhooks/whatsapp` in the BFF feeding
        `ingestWhatsAppStatusWebhook` into a suppression store the dispatcher
        consults, with Meta's signature check failing closed when no app secret
        is set. (12 §6) **Verify:** new
        `apps/oshun/bff/src/routes/whatsapp-status-webhook.spec.ts` — an
        `opted_out` status suppresses the next send to that number; a bad
        signature answers 401.
  - [ ] **P3.c** [V1.0] Mint the FCM token instead of reading a one-hour one:
        `libs/oshun/messaging-channels/src/provider-config-env.ts:106` takes
        `OSHUN_FCM_ACCESS_TOKEN` from the environment. Accept a service-account
        JSON, sign the JWT-bearer grant with `node:crypto`, cache the access
        token and refresh before expiry, mirroring the APNs JWT in
        `transports.ts`. (12 §7) **Verify:** extend
        `libs/oshun/messaging-channels/src/transports.test.ts` — with a fixed
        clock the second send inside the hour reuses the token and a send after
        expiry mints a new one; the token endpoint is a test double.
  - [ ] **P3.d** [V1.1] Prove one live FCM push and one live WhatsApp template
        send. 2026-09-18: needs the owner's Firebase service account and Meta
        Cloud credentials; the FCM leg also needs the native app of V1.1. (12
        §12) **Verify:** a device receives the push and a handset receives the
        template message with the session title in it. `blocked:external`
- [ ] **P4. In-app reminder inbox has no reader** — roll-up: reminders deliver
      into a store no member can see (composes with H3); children below. (12 §8)
  - _Re-checked 2026-09-18: nothing under `apps/oshun/web/src` or
    `apps/oshun/mobile/src` calls `/v1/reminders/inbox`, and
    `apps/oshun/bff/src/routes/notifications.ts` does not merge it._
  - [ ] **P4.a** [V1.0] Merge `readReminderInbox(userId)` into the
        `/v1/notifications` payload as kind `reminder`, with the domain threaded
        through the dispatcher (the gap H3 recorded) and read state through the
        existing read-state store, so
        `apps/oshun/web/src/components/NotificationsCenterPanel.tsx` shows them
        with no client change beyond the new kind. (12 §8) **Verify:** new
        `apps/oshun/bff/src/routes/notifications-reminder-inbox.spec.ts` — a
        produced reminder appears once in `/v1/notifications` for its recipient
        only, and marking it read persists.
  - [ ] **P4.b** [V1.1] Show the same items on the mobile activity tab once H4.a
        has removed its fixtures. 2026-09-18: depends on H4.a. (12 §8)
        **Verify:** the H4.a spec gains a case where a reminder row renders from
        the BFF payload. `blocked:upstream`
- [ ] **P5. Nyx save, follow and remind** — roll-up; the delivery worker now
      reads a durable store, and the route members write through still does not;
      children below. (03 §5)
  - _Partly fixed (checked 2026-09-18): the reminder worker reads
    `app.domainAdapters.nyx.getEventReminders`
    (`apps/oshun/bff/src/server.ts:3490`), which is now the in-process adapter
    over the durable `apps/oshun/bff/src/nyx/nyx-member-stores.ts`._
  - [ ] **P5.a** [V1.0] Make the member's writes land where the worker reads:
        `PUT /v1/nyx/event-actions/:eventId`
        (`apps/oshun/bff/src/routes/nyx.ts:803-818`) uses the module-level `Map`
        in `apps/oshun/bff/src/nyx/event-action-state-store.ts:83`, and in
        production the store is `null` so the route answers 503
        (`nyx.ts:263-264`). Write saves, follows and reminder routes through to
        `nyxMemberStateStore` (or make it the single authority) so the feature
        works in production and survives a restart. (03 §5) **Verify:** new
        `apps/oshun/bff/src/routes/nyx-event-actions-durable.spec.ts` — a
        reminder set through the PUT is returned by `getEventReminders` for that
        member, is absent for another member, and is present after the store is
        re-opened.
- [ ] **P6. Veritas retraction cascade and editorial engines** — roll-up;
      Veritas ships in V1.2, so every child is V1.2; children below. (03 §6–8)
  - _Already fixed (checked 2026-09-18): `USE_MOCK_ADAPTERS=true` is refused in
    production — `assertProductionDomainAdapterSafety`
    (`apps/oshun/bff/src/adapters/domain-adapter-runtime-safety.ts:11-29`),
    called first in `createDefaultDomainAdapters`
    (`apps/oshun/bff/src/app.ts:1763`)._
  - [ ] **P6.a** [V1.2] Give the cascade a customer read path:
        `/v1/veritas/drift` and `/v1/veritas/retractions` are
        `guardedFixtureRoute`s
        (`apps/oshun/bff/src/routes/domain-stubs.ts:2628-2631`). Replace them
        with reads over the cascade records written by
        `apps/oshun/bff/src/veritas/retraction-cascade-runtime.ts`, and show a
        banner on the Veritas briefing and on saved items a cascade touches. (03
        §6) **Verify:** new
        `apps/oshun/bff/src/routes/veritas-retraction-read.spec.ts` — after a
        retraction is posted the read returns it with its scope; before, it
        returns an honest empty list in production mode.
  - [ ] **P6.b** [V1.2] Compute credibility instead of passing it through:
        derive `credibilityTier` from `computeSourceQualityComposite` and
        `bandFromComposite` (`libs/oshun/domain-veritas/src/source-quality/`)
        over the real source records the facade reads. (03 §7) **Verify:** new
        spec beside
        `apps/veritas/api/src/interfaces/http/routes/v1/oshun-facade.ts` — a
        source with recorded assessments gets the band the composite yields; one
        with none reads `unknown`.
  - [ ] **P6.c** [V1.2] Add an admin lane (catalog read plus verb route) that
        drives `EditorialStateMachine`
        (`libs/oshun/domain-veritas/src/editorial/state-machine.ts`) and
        attestation records, following the studio lane-console pattern; if the
        canonical machine of `@oshun/studio-authoring` is used instead, delete
        the Veritas duplicate (this is the second machine P19 counted). (03 §7;
        05 §lifecycle) **Verify:** new
        `apps/oshun/bff/src/routes/admin-veritas-editorial-lane.spec.ts` — an
        illegal transition is refused with the machine's reason; a legal one is
        persisted with the actor from `authContext`.
- [ ] **P7. Sophia verification loops** — roll-up; the customer answer surface
      now shows grounding, and the fact-check, contradiction and
      unsupported-claim loops still run nowhere; children below. (07 §5, §UX)
  - _Already fixed (checked 2026-09-18): the customer answer renders grounding
    state, claim labels and abstention —
    `apps/oshun/web/src/app/sophia/SophiaAskForm.tsx:41-99`._
  - [ ] **P7.a** [V1.0] Run the loops on the live answer path:
        `@sophia/verification` has no BFF consumer. After
        `buildSophiaGroundedAnswer`
        (`apps/oshun/bff/src/routes/domain-stubs.ts`) composes an answer, run
        claim extraction, independent retrieval and claim-to-source matching
        from `libs/sophia/verification/src/`, flag unsupported claims in the
        response, and bind any model call to OpenRouter
        `deepseek/deepseek-v4-flash-0731` in tests. (07 §5) **Verify:** new
        `apps/oshun/bff/src/sophia/verification-loop.spec.ts` — an answer
        containing a claim with no supporting passage in the corpus returns that
        claim flagged unsupported; a supported claim is not flagged.
  - [ ] **P7.b** [V1.0] Feed the operator queue from the loops: persist flagged
        claims and contradictions to Postgres and serve them to the
        evidence-queue panel, replacing the seeded cases and their notice in
        `apps/oshun/web/src/lib/lilith-data/operator-depth.ts:355-361`.
        2026-09-18: depends on P7.a. (07 §5) **Verify:** the panel lists a case
        produced by the P7.a spec scenario and the seed notice is gone.
        `blocked:upstream`
- [ ] **P8. Generation worker, customer gallery and model registry** — roll-up
      of three unconnected generation seams; children below. (07 §4, §6–7)
  - _Partly fixed (checked 2026-09-18): the managed-models registry no longer
    shows seed models — `apps/oshun/bff/src/isis/managed-models-store.ts:1-17`
    is an honest empty projection. The approval-to-registry write is still
    missing (P8.c)._
  - [ ] **P8.a** [V1.0] Add an opt-in interval worker for generation jobs: the
        only drain is the manual `POST /v1/generation/jobs/process`
        (`apps/oshun/bff/src/generation/jobs-route.ts:708`), so a customer
        submission stays `queued` until an operator drains it. Mirror
        `OSHUN_REMINDER_WORKER_INTERVAL_MS` with its own variable, off by
        default, holding the same lease pattern as
        `apps/oshun/bff/src/reminders/cycle-lease.ts`. (07 §7) **Verify:**
        extend `apps/oshun/bff/src/generation/jobs-route.test.ts` — with the
        interval set and a provider double bound, an enqueued job reaches a
        terminal state with no manual drain; with it unset nothing runs.
  - [ ] **P8.b** [V1.0] Bring the customer generation gallery to life:
        `bindGalleryStore` and `bindGalleryContext`
        (`apps/oshun/web/src/app/studio/generation-gallery/loader.ts:43,47`) are
        never called, so every member sees an empty gallery, and the
        `launch-branch`, `send-to-editorial`, `send-to-asset-library` and
        `takedown` buttons (`GenerationGalleryClient.tsx:745-786`) have no
        handler. Bind the loader to the member's released outputs in
        `apps/oshun/bff/src/generation/output-catalog.ts` and wire each button
        to its BFF route. (07 §4) **Verify:** new
        `apps/oshun/web/e2e/studio-generation-gallery.spec.ts` — a released
        output for the signed-in member is listed; takedown removes it after
        confirmation; a member with none sees the empty state.
  - [ ] **P8.c** [V1.0] Write approved Civitai intake into a real registry: bind
        `ManagedModelRegistryStore`
        (`apps/oshun/bff/src/isis/managed-models-store.ts`) to a Postgres table
        and have `applyDecision` in
        `apps/oshun/bff/src/isis/civitai-intake-store.ts` insert a normalized
        record on approve or conditional approve. (07 §6) **Verify:** new
        `apps/oshun/bff/src/isis/managed-models-registry.spec.ts` — an approved
        intake appears in the registry browse with its licence fields; a
        rejected one does not.
- [ ] **P9. Living Scenes share surface** — roll-up; media now survives a
      restart and the download grant has an endpoint, and five share-surface
      findings remain; children below. (08 §7–14)
  - _Already fixed (checked 2026-09-18): share media is in the durable snapshot
    (`apps/oshun/bff/src/routes/living-scenes.ts:296-303`, restored at `:589`)._
  - _Already fixed (checked 2026-09-18): the download grant redeems —
    `GET /v1/living-scenes/public/:shortCode/download-grants/:grantId/media`
    (`living-scenes.ts:2063`) serves the stored bytes in the materialized format
    (`:1940`)._
  - _S12 closed 08 §13; S6 closed the header-borne crisis label of 08 §7._
  - [ ] **P9.a** [V1.0] Stop the shareability matrix passing by construction:
        the share route feeds `resolveShareability` with
        `defaultShareabilityGrants(reachForPrivacy(...))`
        (`apps/oshun/bff/src/routes/living-scenes.ts:1455`), which grants every
        component the requested reach. Derive the `tenant-policy`, `intent` and
        `lilith-policy` grants from their real sources and give the rest the
        rationale "no restricting record". (08 §7) **Verify:** new
        `apps/oshun/bff/src/routes/living-scenes-shareability.spec.ts` — a
        tenant policy of internal-only caps a public request to tenant reach.
  - [ ] **P9.b** [V1.0] Enforce intent redaction on the server: the keep route
        stores the client's `publicRedactedText` verbatim, and `redactIntent`
        (`libs/yemaya/living-scenes-runtime/src/cue-privacy/intent-redaction.ts`)
        has no BFF caller. Recompute `privateIntentHash` and overwrite the
        public layer with `redactIntent(privateText)`. (08 §8) **Verify:** same
        spec file as P9.a — a kept score whose client-supplied public text
        contains a personal name is stored and served redacted.
  - [ ] **P9.c** [V1.0] Serve the first frame: the viewer's OG image, Twitter
        card and reduced-motion still point at
        `/api/living-scenes/<shortCode>/first-frame`
        (`apps/oshun/bff/src/routes/living-scenes.ts:1853,1969,2133`) and no
        handler exists in the BFF or under `apps/oshun/web/src/app`. Add a
        public route that returns the first PNG frame of the stored APNG and
        point the URLs at it. (08 §9) **Verify:** new
        `apps/oshun/bff/src/routes/living-scenes-first-frame.spec.ts` — the
        route returns `image/png` whose bytes are the APNG's first frame; an
        unknown code answers 404.
  - [ ] **P9.d** [V1.0] Burn the visible provenance mark into the pixels:
        neither the share path nor
        `apps/oshun/bff/src/living-scenes/render-route.ts` passes `watermark` to
        the renderer; the short code is only a response header
        (`living-scenes.ts:2402`) and a DOM overlay. Pass
        `visibleMark.shortCode` so `embedWatermarkMark` runs before APNG
        assembly. (08 §12) **Verify:** same spec file as P9.c — decoding a frame
        of a shared scene finds the mark region differing from an unmarked
        render of the same score.
  - [ ] **P9.e** [V1.0] Honour reduced motion: the animated APNG sits outside
        `[data-living-scene-motion-layer]` in
        `apps/oshun/web/src/app/scene/[id]/page.tsx:199-262`, so it keeps
        playing, and `honorsReducedMotionPreference` is a hardcoded `true`
        (`libs/yemaya/living-scenes-runtime/src/personal-artifacts/personal-artifacts.ts:1066`).
        Move the image inside the motion layer, show the first-frame still in
        the reduced-motion branch, and serve a one-frame APNG for
        `rendition: 'reduced-motion'`. 2026-09-18: uses the route of P9.c. (08
        §14) **Verify:** new
        `apps/oshun/web/e2e-inspect/scene-reduced-motion.spec.ts` (chromium,
        `workers=1`, `reducedMotion: 'reduce'`) — the animated image is not
        visible and the still is.
- [ ] **P10. Psyche dialogue surface** — roll-up: a realtime recorder with no
      policy, a client-assignable assistant role and a session route that
      fabricates a connected runtime; children below. (06 §12–13)
  - _Re-checked 2026-09-18: all findings still hold; no web or mobile client
    connects to the surface._
  - [ ] **P10.a** [V1.0] Put member turns under policy:
        `apps/oshun/bff/src/psyche/realtime-route.ts:110-118` accepts
        `role: 'assistant' | 'system'` from the client and records content with
        no `analyzeMessageSafety` and no crisis-frame check. Force
        `role: 'user'` for client turns and mirror the assistant route's
        interception. (06 §12) **Verify:** extend
        `apps/oshun/bff/src/psyche/realtime-route.test.ts` — a client turn
        claiming `assistant` is stored as `user`; a crisis-class turn returns
        the interception envelope and is not stored as ordinary content.
  - [ ] **P10.b** [V1.0] Stop fabricating a connected session:
        `apps/oshun/bff/src/adapters/psyche-embodiment-adapters.ts:296,503,526,580,609`
        returns `connectionState: 'connected'`, join URLs, latencies and
        evaluation scores for transports the capability route reports as not
        built. Fail closed for unbuilt modalities, return only the real
        text-session envelope, delete the invented numbers. (06 §13) **Verify:**
        new `apps/oshun/bff/src/adapters/psyche-session-honesty.spec.ts` —
        requesting a conference modality answers the typed not-built error; the
        text session carries no `latencyMs` or score field.
  - [ ] **P10.c** [V1.0] Give the surface one client or take it off the public
        router: connect the assistant panel through `PsycheSessionBridge`
        (`libs/oshun/shell-assistant/src/psyche-session-bridge.ts`, zero app
        consumers), or mount the realtime route behind a flag that is off in
        production with a dated header note. (06 §12) **Verify:** either a web
        spec shows a panel turn arriving in the dialogue store, or the route
        answers 404 in production mode in `realtime-route.test.ts`.
- [ ] **P11. Crisis residuals** — roll-up: server-side crisis suppression for
      memory and generation, region-aware resources, the post-crisis machinery,
      and one Lilith-reach finding of report 06 that no backlog item carried;
      children below. (06 §14; 11 §10; added 2026-09-18: 06 §19)
  - _Partly fixed (checked 2026-09-18): Living Scenes now consults the frame on
    the server (`isCrisisFrameActiveForUser` in
    `apps/oshun/bff/src/routes/living-scenes.ts`)._
  - [ ] **P11.a** [V1.0] Compute the crisis flag on the server for memory:
        `lilithCrisisFrame` on `/v1/iris/adapter/search`, `plan-write` and
        `remember` is whatever the client sends
        (`apps/oshun/bff/src/routes/iris.ts:704,919`). OR the hint with
        `isCrisisFrameActiveForUser(userId)`. (06 §14) **Verify:** new
        `apps/oshun/bff/src/routes/iris-crisis-suppression.spec.ts` — with an
        armed frame and no client flag, remember writes nothing and search
        returns nothing.
  - [ ] **P11.b** [V1.0] Consult the frame before generation and before a Tara
        session starts: neither the Isis admit path
        (`apps/oshun/bff/src/generation/jobs-route.ts`) nor the Tara routes call
        `isCrisisFrameActiveForUser`. (06 §14) **Verify:** new
        `apps/oshun/bff/src/generation/crisis-frame-admit.spec.ts` — with an
        armed frame enqueue answers the stillness refusal and no job is created.
  - [ ] **P11.c** [V1.0] Make crisis resources region- and language-aware:
        `apps/oshun/bff/src/routes/safety.ts:63` hardcodes `region: 'global'`
        and serves US numbers to everyone. Pass profile region and
        `Accept-Language` into `routeCrisis`
        (`libs/oshun/trust-safety/src/crisis/crisis.ts`) over a region-keyed
        catalog, and fall back to an international directory rather than to US
        numbers. (11 §10) **Verify:** new
        `apps/oshun/bff/src/routes/safety-crisis-region.spec.ts` — a `GB` caller
        gets UK resources, an unmapped region gets the international fallback
        and no US-only number.
  - [ ] **P11.d** [V1.0] Wire the post-crisis machinery: on frame resolution
        persist `planCheckIn` into the reminder dispatcher (opt-in) and consult
        `isInCooldown` in the recommendations route. (11 §10) **Verify:** new
        `apps/oshun/bff/src/safety/post-crisis.spec.ts` — resolving a frame
        schedules one check-in for an opted-in member and none otherwise;
        recommendations are empty during cooldown.
  - [ ] **P11.e** [V1.0] Evaluate reporting obligations: call
        `createMandatoryReport` and `selectLegalContact` on frame resolution,
        producing an operator-visible record; with no legal contact configured
        for the jurisdiction the evaluation fails loud as `not_configured` and
        never claims a report was filed. (11 §10) **Verify:** same spec file as
        P11.d — a jurisdiction with a configured contact yields a pending report
        record; one without yields `not_configured`.
  - [ ] **P11.f** [V1.0] Extend Lilith message safety beyond the assistant:
        `analyzeMessageSafety` is called only from
        `apps/oshun/bff/src/routes/assistant.ts`. Run it on Sophia question
        intake, and run `validateDraftResponse` on composed Sophia output before
        it is sent, reusing the assistant's interception envelope. (06 §19)
        **Verify:** new
        `apps/oshun/bff/src/sophia/sophia-message-safety.spec.ts` — a
        crisis-class question returns the interception envelope with resources
        instead of a composed answer.
- [ ] **P12. Moderation enforcement and integrity** — roll-up: bans that
      restrict nothing, actor identity taken from the request body, and a store
      that forgets on restart; children below. (11 §7–8, §15)
  - _Re-checked 2026-09-18: all three findings still hold._
  - [ ] **P12.a** [V1.0] Enforce bans: nothing outside the moderation files
        reads `banStatus`. Consult it in a preHandler on mutating customer
        routes, map `feature_specific` and `content_specific` scopes to their
        route families, and answer 403 `account_restricted` with the appeal
        link. (11 §7) **Verify:** new
        `apps/oshun/bff/src/moderation/ban-enforcement.spec.ts` — a banned
        member's mutation answers 403 `account_restricted`; reads still work;
        lifting the ban restores access.
  - [ ] **P12.b** [V1.0] Take actor identity from the session:
        `apps/oshun/bff/src/routes/admin-moderation.ts:233,351,424` reads
        `moderatorId`, `reviewerId` and `issuedBy` from the body. Use
        `request.authContext.userId`, refuse an appeal resolution by the
        moderator who made the decision
        (`libs/aja/content-moderation/src/moderation-workflow.ts`), and require
        a second reviewer for permanent bans and P0 classes. (11 §8) **Verify:**
        new `apps/oshun/bff/src/routes/admin-moderation-actor.spec.ts` — a
        body-supplied actor id is ignored; self-review answers 409; a permanent
        ban stays pending until a second operator approves.
  - [ ] **P12.c** [V1.0] Persist the moderation queue, appeals and bans in
        Postgres: `apps/oshun/bff/src/moderation/moderation-store.ts` holds them
        in class fields with no sink. (11 §15) **Verify:** new
        `apps/oshun/bff/src/moderation/moderation-store-durability.integration.spec.ts`
        against local Postgres (`oshun_dev`) — a decision, an appeal and a ban
        survive re-opening the store.
- [ ] **P13. Payments residuals** — roll-up; signed receipts now exist, and the
      crypto invoice contract, refunds and analytics consent remain; children
      below. (11 §11–14)
  - _Already fixed (checked 2026-09-18): every settlement mints an
    Ed25519-signed receipt and refuses to settle without a signer
    (`apps/oshun/bff/src/payments/settlement-route.ts:31,46,198-205`,
    `settlement-receipt.ts`), and the by-id invoice read serves it
    (`apps/oshun/bff/src/routes/domain-stubs.ts:3788-3795`)._
  - _Already fixed (checked 2026-09-18, 11 §19): the settlement HMAC is computed
    over the raw body (`settlement-route.ts:155`)._
  - _2026-09-18: written before the Stripe rail existed. The fiat rail already
    publishes through `FiatEntitlementEmitter`
    (`apps/oshun/bff/src/payments/fiat-routes.ts:529`); P13.a brings the crypto
    rail to the same contract._
  - [ ] **P13.a** [V1.0] Bring the crypto invoice to the spec contract:
        `apps/oshun/bff/src/payments/invoice-store.ts` knows
        `pending | confirmed | expired | cancelled`, the settlement body carries
        no amount, and the plan flip is a direct `updatePlanDurably`
        (`settlement-route.ts:302`). Add `paidAmountAtomic` to the webhook,
        classify settled, underpaid and overpaid through the bridge's
        `state-mapper`, and grant the plan from an entitlement-bus consumer as
        the fiat rail does. (11 §13) **Verify:** extend
        `apps/oshun/bff/src/payments/settlement-route.test.ts` — an underpaid
        settlement grants nothing and flags the invoice; an exact one grants
        through the bus consumer.
  - [ ] **P13.b** [V1.0] Build the refund request path that needs no chain
        access: `POST /v1/payments/invoices/:id/refund-request` (session-scoped,
        destination captured per asset rules), `evaluateRefundEligibility` from
        `@oshun/billing-support`, enqueue on `ColdSpendQueue` from
        `libs/oshun/payments-bridge`, and an admin list-and-initiate route.
        Signing and broadcast stay with the operator's hardware wallet. (11 §14)
        **Verify:** new `apps/oshun/bff/src/payments/refund-request.spec.ts` —
        an eligible invoice produces one queued unsigned transaction; an
        ineligible one answers the typed reason; another member's invoice
        answers 404.
  - [ ] **P13.c** [V1.0] Operator co-signs and broadcasts a queued refund.
        2026-09-18: needs the owner's hardware wallet and a funded node; no
        agent can do it. (11 §14) **Verify:** a test-net refund appears on chain
        and the queue row records the txid. `blocked:external`
  - [ ] **P13.d** [V1.0] Enforce analytics consent: the web transport posts
        unconditionally (`apps/oshun/web/src/analytics/transport.ts`) and the
        ingest consults nothing
        (`apps/oshun/bff/src/analytics/events-ingest.ts`). Gate the shared sink
        on the member's `analytics` flow, and drop events on the server when the
        bearer session resolves to a withdrawn flow. (11 §12) **Verify:** new
        `apps/oshun/bff/src/analytics/events-ingest-consent.spec.ts` — a
        withdrawn member's batch is accepted with zero events stored; a web spec
        asserts no POST after withdrawal.
- [ ] **P14. Localization** — roll-up: eight declared languages and
      right-to-left support over an app that renders English only; children
      below. (13 F5)
  - _Re-checked 2026-09-18: no component under `apps/oshun/web/src` calls
    `useTranslations`; `apps/oshun/web/src/i18n/messages/` contains only `en/`;
    `apps/oshun/web/src/i18n/request.ts:5` hardcodes the default locale._
  - [ ] **P14.a** [V1.0] Make the switcher honest now: in
        `apps/oshun/web/src/components/LanguageSwitcher.tsx` offer only locales
        that have a message catalog, show the rest as coming later and disabled,
        and do not flip the document direction for a locale with no catalog. (13
        F5) **Verify:** new
        `apps/oshun/web/src/components/__tests__/LanguageSwitcher.spec.tsx` —
        with only `en` present every other option is disabled and selecting
        Arabic is impossible.
  - [ ] **P14.b** [V1.0] Thread the stored locale into
        `apps/oshun/web/src/i18n/request.ts`, mount `NextIntlClientProvider`,
        and externalize the shell-level strings (navigation, Home section
        headings, auth, settings) into `messages/en/shell.json` with a
        pseudo-locale proving nothing is hardcoded. (13 F5) **Verify:** new
        `apps/oshun/web/e2e-inspect/i18n-pseudo-locale.spec.ts` (chromium,
        `workers=1`) — under the pseudo-locale the shell chrome on `/`, `/tara`,
        `/nyx`, `/arete`, `/nisaba` shows no unbracketed English string from the
        externalized set.
  - [ ] **P14.c** [V1.0] Draft catalogs for es, fr, de, pt, ja, ar and he from
        the English catalog with the test-bound model (OpenRouter
        `deepseek/deepseek-v4-flash-0731`), each file marked `draft`, and keep
        those locales disabled in the switcher. Quote the spend and ask before
        running it. 2026-09-18: depends on P14.b. (13 F5) **Verify:** a
        key-parity check (`apps/oshun/web/src/i18n/catalog-parity.spec.ts`)
        passes for every drafted locale. `blocked:upstream`
  - [ ] **P14.d** [V1.0] A fluent reviewer signs off each drafted catalog, and
        right-to-left layout is reviewed for ar and he, before its locale is
        enabled. 2026-09-18: a human act. (13 F5) **Verify:** each enabled
        locale's catalog header names its reviewer and date. `blocked:human`
- [ ] **P15. Status page and client observability** — roll-up; mobile crash
      reports now land, and the public status page and the web observability
      directory remain; children below. (13 F3–F4, F6)
  - _Already fixed (checked 2026-09-18): mobile crashes and diagnostics have
    receivers — `POST /v1/mobile/crashes` and `/v1/mobile/diagnostics` in
    `apps/oshun/bff/src/routes/mobile-telemetry.ts:110,143`._
  - [ ] **P15.a** [V1.0] Render the real status:
        `apps/oshun/web/src/app/status/page.tsx` shows the hardcoded
        `STATUS_COMPONENTS` and a "Specimen · fixture data" pill
        (`apps/oshun/web/src/components/lilith/system-pages.tsx:602,658`) while
        `GET /v1/status`
        (`apps/oshun/bff/src/routes/customer-communications.ts`) has no
        consumer. Fetch it in the server component and keep the specimen behind
        a development query parameter. (13 F6) **Verify:** new
        `apps/oshun/web/e2e/status-page-live.spec.ts` — an operator-authored
        incident appears on `/status`; with the BFF down the page says status is
        unavailable instead of showing the specimen.
  - [ ] **P15.b** [V1.0] Decide the web observability directory:
        `apps/oshun/web/src/observability/` posts to `/v1/web/telemetry/*`,
        `/v1/web/errors`, `/v1/web/vitals` and `/v1/web/crashes`, none of which
        exist, and nothing calls its init functions. Add receivers on the
        `mobile-telemetry.ts` pattern and initialise from a client-root effect,
        or delete the directory and route errors through
        `POST /v1/analytics/events`. (13 F3) **Verify:** new
        `apps/oshun/bff/src/routes/web-telemetry.spec.ts` plus a web spec that a
        thrown render error produces one stored report; or
        `ls apps/oshun/web/src/observability` fails and `app/error.tsx` reports
        through the analytics ingest.
- [ ] **P16. Tenant console** — roll-up: no SCIM endpoint, no production login
      for tenant admins, and an export that packages pasted JSON; children
      below. (10 §5, §7, §9)
  - _Re-checked 2026-09-18: all three findings still hold. Tenant session tokens
    are still unsigned and gated with the dev tokens, now in
    `libs/shared/bff-kit/src/authz.ts:216-229,515`._
  - [ ] **P16.a** [V1.0] Expose SCIM 2.0: add `/scim/v2/Users` and
        `/scim/v2/Groups` in `apps/oshun/bff/src/tenant-console/` translating
        through `applyScimSync`
        (`libs/oshun/tenant-console/src/identity/scim.ts`) onto the tenant
        member store, authenticated per tenant with the API-key preHandler of
        T5.a. 2026-09-18: depends on T5.a. (10 §7) **Verify:** new
        `apps/oshun/bff/src/tenant-console/scim-route.spec.ts` — create, update
        and deactivate a user and sync a group; a key for another tenant
        answers 403. `blocked:upstream`
  - [ ] **P16.b** [V1.0] Give tenant admins a production login: mint signed
        HS256 tenant sessions (`tid` plus `tenant:admin:{tid}`) from the SSO
        callback when the connection's tenant has the member, set the
        tenant-admin cookie on a `/handoff` completion route, and stop parsing
        `tenant.` tokens as dev tokens. (10 §9) **Verify:** new
        `libs/shared/bff-kit/src/tenant-session.spec.ts` — with
        `NODE_ENV=production` a signed tenant token authenticates and an
        unsigned `tenant.` token is refused.
  - [ ] **P16.c** [V1.0] Export real tenant data:
        `apps/oshun/bff/src/routes/admin-bulk-exports.ts:62` takes `records`
        from the request body. Add server-side sources per `resourceKind`
        (members, tenant-filtered audit events) and keep pasted records as an
        explicit custom mode. (10 §5) **Verify:** new
        `apps/oshun/bff/src/routes/admin-bulk-exports-sources.spec.ts` — a
        members export for tenant A contains A's members only and its manifest
        verifies.
- [ ] **P17. Tara and Arete remainder** — roll-up; the first child is a
      bright-line honesty defect that H6 handed to this item and the 2026-06-11
      text never listed; children below. (02 §12–17; added 2026-09-18: 02 §5)
  - _Already fixed (checked 2026-09-18): the offerings index shows the member's
    kept and sent rows (`buildAreteOfferingsIndex`,
    `apps/oshun/bff/src/routes/domain-stubs.ts:2111-2124`), the decision log
    became per-member and durable under S1, coach "Approve" posts to
    `/v1/arete/coach/decision`
    (`apps/oshun/web/src/components/lilith/AreteCoachActions.tsx:43`), and the
    real-teacher names are gone from the Tara hub._
  - [ ] **P17.a** [V1.0] Stop the sit player faking a saved reflection:
        `submitReflection` in
        `apps/oshun/web/src/components/lilith/TaraSitPlayer.tsx:391-403` waits
        300 ms and sets `saved` with no request; offline it writes a
        `localStorage` key nothing reads; `moodAfter` is never sent. Add a
        reflection leg to the sitting routes in
        `apps/oshun/bff/src/routes/domain-stubs.ts` carrying
        `{ reflectionText, moodAfter }`, flush the offline queue on mount, and
        show "Saved" only after a 2xx. Work this child first. (02 §5)
        **Verify:** extend
        `apps/oshun/web/src/components/lilith/TaraSitPlayer.test.tsx` — with the
        BFF mocked to fail, the status is never `saved`; with success the stored
        completion has the text and the mood.
  - [ ] **P17.b** [V1.0] Stop the player pausing a still meditator:
        `DRIFT_IDLE_SECONDS = 90` (`TaraSitPlayer.tsx:33,324-331`) fires while
        the session is running, so no sit over 90 seconds completes without a
        tap. Treat running playback as presence, or count drift only from
        `paused`. (02 §12) **Verify:** extend
        `apps/oshun/web/src/components/lilith/TaraSitPlayer.test.tsx` with fake
        timers — a 180-second sitting with no input reaches `completed`.
  - [ ] **P17.c** [V1.0] Replace the Living Offerings fixture composer:
        `/arete/offering` renders `getAreteOfferingFixture()`
        (`apps/oshun/web/src/lib/lilith-data/arete-depth.ts:584-588`, recipient
        "J"). Compose from the member's input and generate through the Living
        Scenes render route once T14.a makes it score-driven. 2026-09-18:
        depends on T14.a. (02 §13) **Verify:** a web spec — the composer starts
        empty, and keeping a composed offering shows it on `/arete/offerings`.
        `blocked:upstream`
  - [ ] **P17.d** [V1.0] Play voice guidance and make the speed slider real:
        `GET /v1/tara/sessions/:id/guidance`
        (`apps/oshun/bff/src/tara/ambient-audio-routes.ts`) has no web caller,
        and `voiceSpeed` (`TaraSitPlayer.tsx:109,636-647`) is read by nothing.
        Fetch guidance at opening, midpoint and closing with a silent skip on
        503, and drive `audio.playbackRate` from the slider. (02 §14)
        **Verify:** extend
        `apps/oshun/web/src/components/lilith/TaraSitPlayer.test.tsx` — the
        guidance URL is requested at the three progress points and the slider
        changes `playbackRate`; a 503 leaves the sit running.
  - [ ] **P17.e** [V1.0] Reach the Arete journal and balance backends from the
        web: the facade serves both, no `/v1/arete/journal` or
        `/v1/arete/balance` BFF route exists, and
        `AreteJournalReflectionWorkspace.tsx` and `AreteLifeBalance.tsx` under
        `apps/oshun/web/src/components/domains/arete/` make no network call. Add
        thin BFF routes over the Arete adapter and wire the editor and the
        assessment submit; fixture entries become the empty state. (02 §15)
        **Verify:** new
        `apps/oshun/bff/src/routes/arete-journal-balance.spec.ts` — a posted
        entry is read back for its author only; a web spec shows it after
        reload.
  - [ ] **P17.f** [V1.0] Wire `/arete/review` to the real week: `getAreteReview`
        returns the fixture (`arete-depth.ts:381-385`) while
        `GET /v1/arete/review` computes highlights from real check-ins, so
        `AreteReviewClose` durably closes the fixture week. Read the route, and
        disable the close action with an honest note when there is no live week.
        (02 §16) **Verify:** new
        `apps/oshun/web/src/lib/lilith-data/__tests__/arete-review-live.spec.ts`
        — the week label comes from the BFF payload; with no check-ins the close
        control is disabled.
  - [ ] **P17.g** [V1.0] Remove the habits fixture fallback: `getAreteHabits`
        returns `getAreteHabitsFixture()` when the BFF read fails
        (`arete-depth.ts:639-643`). Return an unavailable shape the page renders
        as a quiet notice, as `taraUnavailable()` does. (02 §17) **Verify:**
        same spec directory — a failed read yields zero habits and
        `unavailable: true`.
- [ ] **P18. Nisaba and Metis remainder** — roll-up; member state is now
      durable, Nisaba children are V1.0 and Metis children are V1.2; children
      below. (04 §6–11)
  - _Already fixed (checked 2026-09-18): Nisaba member state and integrity
    records are durable — `wireDurableNisabaStudyWorkspaces`
    (`apps/oshun/bff/src/nisaba/state.ts:1292`, wired at
    `apps/oshun/bff/src/server.ts:1929`) and the integrity snapshot
    (`apps/oshun/bff/src/metis/academic-integrity-appeal-store.ts:1592`)._
  - _Already fixed (checked 2026-09-18): the Nisaba room is built around the
    reader's actual daily passage
    (`apps/oshun/bff/src/nisaba/room.ts:147-168`)._
  - [ ] **P18.a** [V1.0] Finish the Nisaba room: `buildNisabaRoom` still ships
        `Cross-references: 4` and `Saved excerpts: 2` as constants
        (`apps/oshun/bff/src/nisaba/room.ts:177-178`), and the no-reader default
        is the Bhikkhu Bodhi translation of SN 22.59 (`:38-39`), which is not
        public domain. Count from the real stores with honest zeros, and replace
        the default with a public-domain rendering that carries its attribution.
        (04 §6) **Verify:** extend `apps/oshun/bff/src/nisaba/room.test.ts` — a
        member with no saved excerpts reads 0; the default passage citation
        names a public-domain edition.
  - [ ] **P18.b** [V1.0] Stop NisabaSurface substituting fixtures in live mode,
        part one: compare targets render `SUPPLEMENTAL_PASSAGE_DETAILS` instead
        of fetching `/v1/nisaba/passages/:id`
        (`apps/oshun/web/src/components/domains/NisabaSurface.tsx:567`), a live
        but empty notebooks response is replaced by
        `FALLBACK_NOTEBOOK_LIST_RESPONSE` (`:706,1175`), and a BFF error shows
        the embedded fixture labelled "Cached Nisaba workspace" (`:2834`). Fetch
        real bodies, render honest empty, label the error as unavailable. (04
        §7) **Verify:** new
        `apps/oshun/web/src/components/domains/__tests__/NisabaSurfaceLiveHonesty.spec.tsx`
        — empty notebooks render the empty state; an error renders no passage
        body; a compare target shows the fetched body.
  - [ ] **P18.c** [V1.0] Part two: remove the fixture grounded views and the
        unconditional `SAVED_PASSAGE` card (`NisabaSurface.tsx:367,1358`); wire
        `/v1/nisaba/adapter/saved-passages` or drop the card. (04 §7)
        **Verify:** same spec file — with no saved passages the card is absent
        and no view claims `grounded`.
  - [ ] **P18.d** [V1.0] Lift the Nisaba preview banner: set
        `DOMAIN_PREVIEW_SURFACES.nisaba` to `false` in
        `apps/oshun/web/src/components/domains/DomainPreviewBanner.tsx`.
        2026-09-18: only after P18.b and P18.c. (04 §7) **Verify:** the existing
        domain-surface banner tests are updated and pass with Nisaba unbannered.
        `blocked:upstream`
  - [ ] **P18.e** [V1.0] Make the Nisaba depth pages honest about the reader:
        `apps/oshun/web/src/lib/lilith-data/nisaba-depth.ts:39,651` presents
        "Renata" marginalia and `dayNumber: 109` as the member's own, and
        `apps/oshun/web/src/app/nisaba/notebooks/page.tsx:57,86` swaps in
        `NOTEBOOK_FIXTURE` with timestamps computed from today whenever the BFF
        refuses. Render the member-shaped regions from the real stores or empty;
        keep the curated apparatus. (04 §10) **Verify:** new
        `apps/oshun/web/src/lib/lilith-data/__tests__/nisaba-depth-honesty.spec.ts`
        — no output contains `Renata`; a refused notebooks read yields an
        unavailable state with zero notebooks.
  - [ ] **P18.f** [V1.2] Make source approval gate generation: nothing on the
        ingest or generation path reads
        `apps/oshun/bff/src/metis/metis-byom-decision-store.ts`. For high-stakes
        outputs refuse with `source_not_approved` unless the latest decision for
        the source is approved, and list which outputs count in the route
        header. (04 §8) **Verify:** new
        `apps/oshun/bff/src/metis/byom-approval-gate.spec.ts` — a rejected
        source cannot produce a course outline; an approved one can.
  - [ ] **P18.g** [V1.2] Drive `/metis/tutor` from the real session-memory
        store: `getMetisTutor` returns the fixture transcript
        (`apps/oshun/web/src/lib/lilith-data/metis-depth.ts:74-78`) and no
        product surface posts turns to `POST /v1/metis/tutor-session-memories`.
        List the member's sessions, post turns, and show honest empty otherwise.
        (04 §9) **Verify:** new `apps/oshun/web/e2e/metis-tutor-live.spec.ts` —
        a posted turn appears after reload; a new member sees no transcript.
- [ ] **P19. Editorial lifecycle, embargo and the unused-engine census** —
      roll-up; one of the two lifecycle machines now has a consumer; children
      below. (05 §lifecycle/embargo/census)
  - _Partly fixed (checked 2026-09-18): the canonical machine is consumed —
    `apps/oshun/bff/src/agentic/autonomy-bindings/editorial-publisher.ts:19`
    advances through `tryEditorialAdvance`. The Veritas duplicate is handled in
    P6.c._
  - [ ] **P19.a** [V1.0] Enforce embargo and blocked streams:
        `apps/oshun/bff/src/admin/admin-editorial-release-streams-store.ts` is
        read only by its own routes. Have the communications publish path and
        the generation release leg refuse or delay a subject with an open
        `embargo` or `blocked` stream. (05 §embargo) **Verify:** new
        `apps/oshun/bff/src/admin/release-stream-enforcement.spec.ts` —
        publishing an embargoed subject before `embargoLiftsAt` is refused;
        after it, allowed.
  - [ ] **P19.b** [V1.0] Re-run the census and write the recommendation: for
        each family with no consumer in `@oshun/studio-authoring`
        (creator-roles, asset-metadata, templates, localization-workflow,
        versioning, taxonomy-curation, collaboration) and
        `@oshun/customer-curation` (collections, annotations, share-cards,
        version-awareness), record today's importer count and recommend
        wire-for-V1.0 or defer, in
        `docs/audits/V1_RESIDUAL_AUDIT_2026-06-11/05-editorial-studio.md` under
        a dated heading. (05 §census) **Verify:** the dated section exists and
        every family row carries a `grep` command and its result.
  - [ ] **P19.c** [V1.0] The owner records build or defer per family.
        2026-09-18: a scope decision; the report notes that no recorded deferral
        covers these spec sections. (05 §census) **Verify:** each family has a
        dated decision line in `V1/TODOS.md`. `blocked:governance`
- [ ] **P20. Agentic remainder** — roll-up; the console field misread is fixed,
      and the registry, catalog, pipelines and evaluation findings remain;
      children below. (09 F7–F11)
  - _Already fixed (checked 2026-09-18): approved runs read as approved —
    `apps/oshun/bff/src/routes/admin-agentic-operations.ts:111-112` uses
    `latestApproval.verdict`._
  - [ ] **P20.a** [V1.0] Give gold sets and champion-challenger something to
        operate on: the admin store has promote and rollout verbs and no create
        verb, production seeds are empty, and nothing reads `trafficSharePct`.
        Add create and ingest routes, and have run dispatch consult the active
        experiment for agent-version selection; or label the panels read-only
        and record the deferral in the route header. (09 F8) **Verify:** extend
        the specs beside
        `apps/oshun/bff/src/admin/admin-agentic-operations-store.ts` — a created
        experiment at 100% share selects the challenger version on the next run.
  - [ ] **P20.b** [V1.0] Validate tool plans against the contract catalog and
        check grants: the execute route accepts any id in the injected four-tool
        registry and `V1_TOOL_CATALOG` (`libs/contracts/src/agent/tools.ts`) has
        no runtime reader. Unknown id answers 400, known but unimplemented
        answers 503, and admission resolves a server-side grant keyed by agent,
        tool and scope. (09 F9) **Verify:** extend
        `apps/oshun/bff/src/agentic/runs-route.test.ts` — the three outcomes
        above, plus a revoked grant refusing a previously allowed tool.
  - [ ] **P20.c** [V1.0] Validate `pipelineId` against `V1_PIPELINES`
        (`libs/oshun/agentic-studio/src/pipelines/v1-pipelines.ts`) at submit
        and attach the pipeline's declared approval gate and budget cap to the
        run. (09 F10) **Verify:** extend
        `apps/oshun/bff/src/agentic/run-lifecycle-store.test.ts` — an unknown
        pipeline answers 400; `arete.weekly_review_draft` starts in
        `awaiting_approval` with its cap.
  - [ ] **P20.d** [V1.0] Mount a read-only agent registry over
        `libs/oshun/agentic-studio/src/registry/` seeded with the agents the
        product invokes, and make submit and execute refuse an unregistered
        `rootAgentId`; this also makes the per-agent kill switches of S8
        airtight. (09 F11) **Verify:** extend
        `apps/oshun/bff/src/agentic/runs-route.test.ts` — an unregistered agent
        id answers 400; a per-agent switch stops a registered one.
- [ ] **P21. Contracts under the routes** — roll-up: bind the contract library
      to request and response payloads and make OpenAPI drift checking two-way;
      children below. (13 F9, F15)
  - _Re-counted 2026-09-18: 527 route files under `apps/oshun/bff/src/routes`,
    43 import `@oshun/contracts`; the OpenAPI document lists about 108 `/v1/`
    paths._
  - [ ] **P21.a** [V1.0] Add a `parseWith(schema)` helper (or
        `fastify-type-provider-zod`) in `libs/shared/bff-kit/src/` and a lint
        rule that fails a new route file which reads `request.body` without it.
        (13 F9) **Verify:** new `libs/shared/bff-kit/src/parse-with.spec.ts`;
        the rule fires on a fixture file in the ESLint rule's own test.
  - [ ] **P21.b** [V1.0] Migrate the first tranche: every mutation route in the
        auth, profile, consent, payments and library families validates its body
        and shapes its response through a contracts schema; record the before
        and after counts in the item when done. (13 F9) **Verify:**
        `grep -L "@oshun/contracts"` over that tranche's route files prints
        nothing, and each family's existing route tests pass per file with
        `npx vitest run <file>`.
  - [ ] **P21.c** [V1.0] Validate analytics ingest with the library it ships
        beside: `apps/oshun/bff/src/analytics/events-ingest.ts` hand-rolls type
        checks while `validateEventEnvelope`
        (`libs/oshun/analytics/src/validation.ts`) and the event-name taxonomy
        go unused. (13 F9) **Verify:** extend
        `apps/oshun/bff/src/analytics/events-ingest.test.ts` — an event name
        outside the taxonomy is rejected with the taxonomy's reason.
  - [ ] **P21.d** [V1.0] Add the inverse OpenAPI assertion to
        `apps/oshun/bff/src/__tests__/contract/openapi-coverage.test.ts`:
        enumerate the app's registered routes and fail for any that is neither
        documented nor on an explicit waiver list with a reason. (13 F15)
        **Verify:** the test fails when a new undocumented route is registered
        and passes once it is documented or waived.
- [ ] **P22. Workspace and cross-device read-back** — roll-up: sync is
      write-only, plus one workspace handoff finding of report 01 that no
      backlog item carried; children below. (01 §19, §21; 04 §13; added
      2026-09-18: 01 §18)
  - _Re-checked 2026-09-18: the only `pushClientState` call is
    `apps/oshun/web/src/components/workspace/MultiPanelWorkspace.tsx:446` and
    `pullClientState` has no caller._
  - [ ] **P22.a** [V1.0] Read the durable slot back on boot: call
        `pullClientState('workspace-layout')` and merge by `updatedAt` when
        `MultiPanelWorkspace` mounts. (01 §21) **Verify:** new
        `apps/oshun/web/e2e/workspace-layout-cross-device.spec.ts` — a layout
        saved in one browser context is restored in a second context for the
        same member.
  - [ ] **P22.b** [V1.0] Bring the `study-plan-drafts` slot to life:
        NisabaSurface persists notes, drafts, collections and plans to
        `localStorage` only (`apps/oshun/web/src/lib/nisaba-offline.ts`). Push
        them through `pushClientState('study-plan-drafts', …)` on persist and
        pull and merge in the hydration effect. (04 §13) **Verify:** same spec
        pattern — a study plan written in one context appears in a second.
  - [ ] **P22.c** [V1.1] Point mobile Explore at real search:
        `apps/oshun/mobile/app/(tabs)/explore.tsx:264` searches an in-file index
        with invented freshness and trending and makes no network call. Use
        `GET /v1/search` through the typed BFF client, keep curated rows
        labelled as editorial, drop "trending" until the analytics ingest can
        supply it. (01 §19) **Verify:** new
        `apps/oshun/mobile/app/(tabs)/explore-live-search.spec.tsx` (jest) — a
        query issues one `/v1/search` call and renders its results; offline
        shows an unavailable state.
  - [ ] **P22.d** [V1.0] Stop the workspace-to-mobile handoff packet inventing
        its header:
        `apps/oshun/web/src/components/workspace/irisMobileWorkspaceHandoff.ts:46,50,60`
        sends a static user id, `consentState: 'granted'` and fixed memory
        counts, and adds voice-session and transcript items that do not exist.
        Fill the header from `/v1/iris/adapter/continuity` and the real consent
        read, use the session user, and include those items only when a real id
        exists. (01 §18) **Verify:** new
        `apps/oshun/web/src/components/workspace/__tests__/irisMobileWorkspaceHandoff.spec.ts`
        — with consent ungranted the packet says ungranted and carries no
        invented counts or items.

## Phase Q — P2 + UX polish register (after P; see reports for detail)

Work V1.0 children first, then V1.1, then V1.2.

- [ ] **Q1. P2 and UX polish sweep** — roll-up of the lower-severity findings of
      all thirteen reports, one child per finding, grouped by report number
      (`Q1.<report>.<letter>`); children below. (reports 01–13, P2 and UX
      sections)
  - _Already fixed (checked 2026-09-18) — 01 §27: the voice-input bars read the
    microphone (`analyser.getByteFrequencyData`,
    `apps/oshun/web/src/components/assistant/AssistantPanel.tsx:4204-4205`)._
  - _Already fixed — 03 §12: the Veritas home briefing degrades to its own 503
    (`apps/oshun/bff/src/routes/veritas.ts:758-770`)._
  - _Already fixed — 04 §16: the Nisaba and Metis adapters run in-process
    (`apps/oshun/bff/src/app.ts:1788-1802`), so their reads and the Sophia
    retriever no longer dial a missing service._
  - _Already fixed — 06 §24: assistant sessions are durable
    (`wireDurableAssistantSessions`, `apps/oshun/bff/src/server.ts:225`). 06
    §26: `/welcome/test-assistant` is gated by `TestHarnessGate`
    (`apps/oshun/web/src/app/welcome/test-assistant/layout.tsx`)._
  - _Already fixed — 07 (lineage seeds):
    `apps/oshun/bff/src/isis/output-lineage-store.ts:1-25` is an honest empty
    projection; what is left is Q1.07.a._
  - _Already fixed — 11 §19 (HMAC over the raw body): see P13. 12 §9: Telegram
    `/stop` is enforced in the reminder cycle
    (`apps/oshun/bff/src/reminders/reminders-route.ts:737-739`,
    `apps/oshun/bff/src/telegram/delivery-stop.spec.ts`), and `/voice` has a
    fail-closed TTS provider
    (`libs/oshun/messaging-channels/src/telegram/tts-provider.ts`)._
  - _Already fixed — 10 §10 (audit tamper evidence):
    `apps/oshun/bff/src/admin/admin-audit-events-store.ts` seals each event with
    an HMAC and the previous event's hash (`sealEvent`, `:138-159`),
    `verifyIntegrity` (`:359`) is returned by
    `apps/oshun/bff/src/routes/admin-audit-log.ts:120,146`, and production
    refuses to boot without `OSHUN_ADMIN_AUDIT_INTEGRITY_KEY`
    (`apps/oshun/bff/src/admin/admin-audit-governance.ts:26-36`). What is left
    is Q1.10.a._
  - _Already fixed — 11 §17 (retention schedules): canonical rules live in
    `libs/contracts/src/common/data-retention-rules.ts` (conversation history 30
    days) and `runComplianceRetention` sweeps about thirty partitions with
    legal-hold checks (`apps/oshun/bff/src/server.ts:957-1250`, scheduled at
    `:2075` and `:2957`). What is left is Q1.11.b._
  - _Already fixed — 12 §10 (channel audit envelopes): the Telegram webhook
    persists them (`recordTelegramAuditResponses`,
    `apps/oshun/bff/src/routes/telegram.ts:720-728`)._
  - _Pointers, not tasks: "webhook secrets volatile" (10 §11) is T6.b; "no
    billing view" (11 §21) is H2.f; 02 §22 and 06 §27 resolve with P17, T2.a,
    T3.c and T15.a; 08 §24 (latency budgets) waits for the conductor decision
    T14.c._
  - [ ] **Q1.01.a** [V1.0] Unfreeze the account switcher clock:
        `ACCOUNT_SWITCHER_NOW_MS = Date.parse('2026-05-07…')`
        (`apps/oshun/web/src/components/ShellLayout.tsx:301`, used at `:1100`).
        Capture `Date.now()` after mount as the file already does for
        `shellMountedAtMsRef`. (01 §24) **Verify:** new
        `apps/oshun/web/src/components/__tests__/ShellLayoutAccountSwitcherClock.spec.tsx`
        with fake timers — an account active one hour ago reads as an hour, not
        months.
  - [ ] **Q1.01.b** [V1.0] Replace the stale "What's New":
        `apps/oshun/web/src/components/WhatsNewDropdown.tsx` serves a
        hand-written changelog frozen at v0.8–0.9 ("Dark theme polish", `:82`).
        Serve entries from the authored-communications store with a `changelog`
        kind, or show nothing. (01 §23) **Verify:** extend
        `apps/oshun/web/src/components/activity/WhatsNewFeed.spec.tsx` or add a
        dropdown spec — entries come from the mocked communications read; an
        empty read renders no list.
  - [ ] **Q1.01.c** [V1.0] Delete the unused fixture dashboards beside Home:
        `KpiGrid` (`apps/oshun/web/src/components/home/KpiGrid.tsx:522`
        hardcodes `sessions = 28`) and `CrossDomainHub`. Confirm no page imports
        them, then remove the components and their tests. (01 §25) **Verify:**
        `grep -rn "KpiGrid\|CrossDomainHub" apps/oshun/web/src/app` prints
        nothing and the files are gone.
  - [ ] **Q1.01.d** [V1.1] Persist mobile preferences:
        `apps/oshun/mobile/src/profile/store.ts:21` builds every profile,
        preference and privacy store on `createMemoryKeyValueStorage()`, so each
        setting resets on restart. Back them with the app's secure or async
        storage. (01 §26) **Verify:** new
        `apps/oshun/mobile/src/profile/store-persistence.spec.ts` (jest) — a
        preference written, the store re-created over the same storage, the
        preference read back.
  - [ ] **Q1.01.e** [V1.2] Stop Home saying "Resume course" to a member who
        never started one: `ctaLabel: 'Resume course'` in
        `apps/oshun/web/src/components/home/homeMetisStudyContinuationModels.ts:53`
        is the fallback label. (01 §28) **Verify:** extend
        `apps/oshun/web/src/components/home/__tests__/HomeMetisStudyContinuationSection.test.tsx`
        — with no continuation the label invites starting, not resuming.
  - [ ] **Q1.01.f** [V1.0] Give the Home modules one degraded vocabulary: after
        H1.e–g land, every module uses the Veritas pattern (says what is
        unavailable, claims nothing) and "Curated" is kept for editorial content
        that makes no personal or factual claim. 2026-09-18: depends on H1.e,
        H1.f, H1.g. (01 §29) **Verify:**
        `grep -rn "Curated" apps/oshun/web/src/components/home` hits only
        editorial modules; one spec renders all modules degraded and asserts the
        shared presentation. `blocked:upstream`
  - [ ] **Q1.02.a** [V1.0] Decide the unused Tara content plumbing:
        `use-tara-content.ts`, `tara-cache.ts`, `tara-monitoring.ts` and
        `content-filters.ts` in `apps/oshun/web/src/lib/tara/` have no importer.
        Point the hooks' query functions at the real `/v1/tara/*` facade routes
        and adopt them in the deep components, or delete the four modules. (02
        §18) **Verify:** each module has a component importer, or the four files
        and their tests are gone.
  - [ ] **Q1.02.b** [V1.0] Fix the Tara hub copy: the eyebrow is the literal
        "Today's sit · 12 min"
        (`apps/oshun/web/src/components/lilith/rooms.tsx:102`) beside a Begin
        link that computes the real minutes, and `TARA_TODAY_FIXTURE` in
        `apps/oshun/bff/src/routes/domain-stubs.ts` describes a path that
        contradicts `SITTING_PATH`. Render the real duration and derive the
        fixture from the room builder. (02 §19) **Verify:** new
        `apps/oshun/web/src/components/lilith/rooms-tara-duration.spec.tsx` — a
        180-second sitting shows 3 min in both places.
  - [ ] **Q1.02.c** [V1.0] Refuse unknown sitting ids:
        `POST /v1/tara/sittings/:id/complete`
        (`apps/oshun/bff/src/routes/domain-stubs.ts:1634`) records any string,
        which inflates streaks. Answer 404 unless the id is in the catalog
        exported from `apps/oshun/bff/src/tara/room.ts`. (02 §20) **Verify:**
        new `apps/oshun/bff/src/routes/tara-sitting-catalog-guard.spec.ts` — a
        made-up id answers 404 and writes nothing; a catalog id records once.
  - [ ] **Q1.02.d** [V1.0] Give the dead Tara hub tabs destinations: "Courses",
        "Teachers" and "Library"
        (`apps/oshun/web/src/components/lilith/rooms.tsx:72-74`) have no `href`;
        repoint the Arete hub habit links and "New ritual" at `/arete/habits`
        and `/arete/habits/new`. (02 §21) **Verify:**
        `apps/oshun/web/e2e-inspect/` keyboard walk — each tab is a link that
        lands on a 200 page.
  - [ ] **Q1.02.e** [V1.0] Tell failure from emptiness:
        `apps/oshun/web/src/components/domains/tara/TaraAnalyticsDashboard.tsx`
        keeps its empty model when the fetch fails, and
        `apps/oshun/web/src/app/arete/offerings/page.tsx` renders a refused read
        as "No drafts in progress". Track a failed state and render a quiet
        could-not-load notice. (02 §23) **Verify:** new
        `apps/oshun/web/src/components/domains/tara/TaraAnalyticsDashboard-failed.spec.tsx`
        and a page spec for the offerings index — a rejected fetch renders the
        notice and not the empty-state copy.
  - [ ] **Q1.03.a** [V1.0] Remove the dead hydration path:
        `hydrateDomainStoresFromBff`
        (`apps/oshun/web/src/lib/stores/domain-stores.ts:622`) calls four
        `/v1/domains/*/state` routes that do not exist and has no caller. Delete
        it with the `lastSyncedAt` fields that imply a sync, or re-point it at
        `/v1/client-state/:stateKey`. (03 §9) **Verify:**
        `grep -rn hydrateDomainStoresFromBff apps/oshun/web/src` prints nothing,
        or a spec shows a real round trip.
  - [ ] **Q1.03.b** [V1.2] Give Veritas saves a server path: topic follow now
        has routes (`apps/oshun/bff/src/routes/veritas.ts:405,424`) but there is
        no save-article mutation, so "Save to notebook" writes `localStorage`
        only and never reaches the workspace evidence panel or achievements. Add
        `POST` and `DELETE /v1/veritas/adapter/saved-articles/:articleId` over
        the adapter and write through from `VeritasArticleActions.tsx`. (03 §10)
        **Verify:** new
        `apps/oshun/bff/src/routes/veritas-saved-articles-write.spec.ts` — a
        saved article appears in `/v1/veritas/adapter/saved-articles` for that
        member only.
  - [ ] **Q1.03.c** [later] Replace the 2024 sample data in the standalone Nyx
        service: `apps/nyx/api/src/services/events-service.ts:10-20` and the
        January-2024 TLE in `satellites-service.ts:25`. This service is off the
        V1.0 path (the BFF's Nyx adapter runs in-process); back `/api/v1/events`
        with the `@oshun/domain-nyx` solver and make satellites fail closed as
        `not_configured` without a fresh TLE feed. (03 §11) **Verify:**
        `npx vitest run` on the service's events spec — `getUpcomingEvents` for
        today's date returns future events; satellites without a feed answer
        `not_configured`.
  - [ ] **Q1.03.d** [V1.0] Fix the Nyx hub navigation: "Planets" points at the
        satellite tracker and "Observations" at the simulated log
        (`apps/oshun/web/src/components/lilith/rooms.tsx:1059-1061`) although
        the member's real log lives at `/nyx/observation`. Point Observations at
        the real route and rename or re-target Planets. (03 §13) **Verify:** a
        rooms spec asserts the two hrefs.
  - [ ] **Q1.04.a** [later] Give the Nisaba scholarship engines their first real
        consumer: run the collation engine
        (`libs/nisaba/criticism/src/collation-engine.ts`) over the corpus
        compare pairs on the server to produce the `alignments` the compare view
        shows, which are curated text in `apps/oshun/bff/src/nisaba/state.ts`
        today. The full Nisaba service is a recorded later-release deferral;
        this is the optional pre-step the report names. (04 §12) **Verify:** new
        `apps/oshun/bff/src/nisaba/compare-collation.spec.ts` — alignments for
        one compare pair equal the engine's output.
  - [ ] **Q1.04.b** [V1.0] Make the illustrative manuscript witness match its
        passage: the witness for the Dhammapada I.1–2 passage transcribes a
        breath-mindfulness text (`Anapanassatim bhikkhave…`,
        `apps/oshun/bff/src/nisaba/state.ts:1378`). Use the Pali of Dhammapada
        I.1–2 from a public-domain edition with its attribution, or bind the
        witness to a passage it matches. (04 §15) **Verify:** new
        `apps/oshun/bff/src/nisaba/witness-coherence.spec.ts` — the witness
        transcription shares its opening words with the passage's source text.
  - [ ] **Q1.04.c** [V1.2] Align the Metis assistant seeds with the
        six-discipline focus:
        `apps/oshun/bff/src/routes/metis-search-seeds.ts:14,22` leads with
        Calculus and Data Structures and links to `/courses/...` routes that do
        not exist. Use the room's curated catalog as the one source and link
        only to routes that exist. (04 §14) **Verify:** new
        `apps/oshun/bff/src/routes/metis-search-seeds.spec.ts` — every seed href
        resolves against the web route manifest and every seed discipline is one
        of the six.
  - [ ] **Q1.05.a** [V1.0] Let reading progress change:
        `apps/oshun/bff/src/nisaba/state.ts` serves `progressPercent: 0` and no
        route ever updates it. Add `POST /v1/nisaba/passages/:id/progress` (or
        fold it into the client-state sync) and send it from the reader. (05
        §reading progress) **Verify:** new
        `apps/oshun/bff/src/routes/nisaba-reading-progress.spec.ts` — posting 40
        then reading the passage returns 40 for that member and 0 for another.
  - [ ] **Q1.05.b** [V1.0] Make the collections copy true:
        `apps/oshun/web/src/app/library/collections/page.tsx:77` promises "Drag
        and drop, reorder". Wire the ordering engine of
        `@oshun/customer-curation` into the dashboard's item list, or soften the
        copy. (05 §copy) **Verify:** the sentence is gone, or an e2e spec
        reorders two items and the order persists.
  - [ ] **Q1.05.c** [V1.0] Fix the collection deep links:
        `apps/oshun/web/src/app/library/collections/page.tsx:100` and
        `new/LibraryNewCollectionForm.tsx:130` link to
        `/library?collectionId=…`, which `/library` ignores; the working route
        is `/library/collection/[collectionId]`. 2026-09-18: a BFF collection id
        only resolves once T13.a lands. (05 §deep links) **Verify:** an e2e spec
        — clicking a collection on the index opens that collection.
        `blocked:upstream`
  - [ ] **Q1.05.d** [V1.0] Show Telegram `/save` captures somewhere:
        `GET /telegram/captures` has no reader in web, mobile or the Mini App.
        Feed captures into the saved-items store, or add a Telegram lane to the
        library. (05 §telegram) **Verify:** new
        `apps/oshun/bff/src/telegram/captures-in-library.spec.ts` — a capture
        appears in the member's saved items and in universal search.
  - [ ] **Q1.05.e** [V1.0] Lift annotations out of Nisaba: move the per-user
        store to one keyed by surface and artifact id using the
        `@oshun/customer-curation` annotations engine, keep the Nisaba routes
        working over it, and mount it on the Tara transcript reader. Veritas and
        Metis readers follow in V1.2. (05 §annotations) **Verify:** new
        `apps/oshun/bff/src/routes/shared-annotations.spec.ts` — an annotation
        on a Tara transcript and one on a Nisaba passage are listed together for
        their author and export together.
  - [ ] **Q1.05.f** [V1.0] Persist the authored document and carry its id
        through the studio lanes: `/studio/authoring` holds its document in
        memory (H12) and the review, comments and localization consoles evaluate
        hand-pasted JSON. Add a Postgres artifact store for authored documents
        and let each lane accept an artifact id as its default payload. (05
        §authoring, §lanes) **Verify:** new
        `apps/oshun/bff/src/studio/authored-artifact-store.integration.spec.ts`
        against local Postgres (`oshun_dev`) — a saved document is re-opened by
        id and submitted to the approval-workflow route by id.
  - [ ] **Q1.06.a** [V1.0] Render the disclosure the server already sends: the
        assistant route emits `responseMode`, `retrievalText` and the server
        `activePersona`, and no client reads them. Show a
        grounded-or-synthesized chip and the server's persona label in
        `apps/oshun/web/src/components/assistant/AssistantPanel.tsx`. (06 §22)
        **Verify:** extend
        `apps/oshun/web/src/components/assistant/__tests__/AssistantPanel.test.tsx`
        — a reply with `responseMode: 'synthesized'` shows the chip.
  - [ ] **Q1.06.b** [V1.0] Unfreeze the Lilith adapter and stop inventing voice
        scores: `DEFAULT_NOW = '2026-03-22…'` stamps health forever
        (`apps/oshun/bff/src/adapters/lilith-persona-policy-adapters.ts:36,122`)
        and `createVoiceSafetyAssessment` returns
        `clarity: 0.94, naturalness: 0.92` (`:477-478`). Use the live clock, and
        answer `{ assessed: false, reason: 'no-audio-analysis-provider' }` until
        a real scorer exists. (06 §23) **Verify:** new
        `apps/oshun/bff/src/adapters/lilith-persona-policy-honesty.spec.ts` —
        `lastChecked` is within a second of now and the assessment carries no
        numeric score.
  - [ ] **Q1.06.c** [V1.0] Measure or drop the health latencies:
        `apps/oshun/bff/src/adapters/iris-memory-adapters.ts:846-858` reports
        constants (9, 7, 12, 5, 6, 11 ms). Time the store call as the Psyche
        adapter does, or remove the field. (06 §25) **Verify:** new
        `apps/oshun/bff/src/adapters/iris-health-latency.spec.ts` — two health
        reads with a slowed store differ, or the field is absent.
  - [ ] **Q1.07.a** [V1.0] Show real lineage in the operator console: released
        outputs record lineage in
        `apps/oshun/bff/src/generation/output-catalog.ts`, and the console reads
        the unbound `apps/oshun/bff/src/isis/output-lineage-store.ts`. Bind the
        console to the catalog's lineage, with ownership, so a takedown can
        compute its cascade. (07 lineage) **Verify:** new
        `apps/oshun/bff/src/isis/output-lineage-binding.spec.ts` — releasing a
        derived output makes the parent-to-child edge appear in the console
        read.
  - [ ] **Q1.07.b** [V1.0] Verify the watermark before narration plays: no
        customer playback component checks the provenance bundle's watermark
        hash. Add the check at playback entry and block or flag on mismatch. (07
        watermark) **Verify:** new
        `apps/oshun/web/src/components/studio/generation/NarrationCard-watermark.spec.tsx`
        — a tampered hash prevents `play()` and shows the flag; an intact one
        plays.
  - [ ] **Q1.08.a** [V1.0] Replace the dead accessibility URLs: captions, audio
        description, stills and transcript links use an unresolvable
        `oshun://living-scenes/…` scheme and `burnInAvailable: true` is
        hardcoded
        (`libs/yemaya/living-scenes-runtime/src/personal-artifacts/personal-artifacts.ts:1019-1059`).
        Generate a real VTT from the transcript lines and serve it beside the
        transcript route; report audio description as unavailable. (08 §15)
        **Verify:** new
        `apps/oshun/bff/src/routes/living-scenes-captions.spec.ts` — the caption
        URL returns `text/vtt` with the transcript's cues; no response contains
        `oshun://`.
  - [ ] **Q1.08.b** [V1.0] Make the pre-share re-check a real one:
        `containsBlockedSafetyFrame` (`personal-artifacts.ts:414-420`) is a
        six-phrase English substring list recorded as the Lilith re-check. Send
        the score text through the BFF's Lilith moderation seam, record its
        verdict and true policy version, and keep the list as a pre-filter. (08
        §16) **Verify:** new
        `apps/oshun/bff/src/routes/living-scenes-preshare-check.spec.ts` — a
        non-English crisis phrase the list misses is caught, and the share
        record names the policy version used.
  - [ ] **Q1.08.c** [V1.0] Reconcile the Nyx cue verbs inside the spec: the
        finding (code diverges from the spec's per-template verbs) is overtaken,
        because the canonical table in
        `V1/features/domain-templates-and-editor.md:58-64` now lists exactly the
        sets in `TEMPLATE_ALLOWED_VERBS`
        (`libs/yemaya/living-scenes-runtime/src/customer-card/customer-card.ts:108-147`).
        What is left is that the Nyx prose at `:119-120` of the same spec file
        still names `shape-by`, which the table and the code do not grant. Make
        prose and table agree, and pin the table with a test. (08 §17)
        **Verify:** extend
        `libs/yemaya/living-scenes-runtime/src/customer-card/customer-card.test.ts`
        — each template's allowed set equals the row of the spec table, asserted
        literally; the spec file has one answer for Nyx.
  - [ ] **Q1.08.d** [V1.0] Remove the invented cost estimate: `costUsd` and
        `latencySeconds` are computed from the length of the domain id
        (`customer-card.ts:304-309`). Use honest static per-template figures
        with their source, or drop the fields. (08 §18) **Verify:** extend
        `customer-card.test.ts` — no estimate depends on `domainId.length`.
  - [ ] **Q1.08.e** [V1.0] Use one technique catalog: compose-assist embeds its
        own allowlists
        (`libs/yemaya/living-scenes-runtime/src/compose-assist/compose-assist.ts`)
        while the full catalog in
        `libs/yemaya/blend-kernel/src/catalog/cinematographic-catalog.ts` has no
        app consumer. Make compose-assist read the catalog and validate the
        catalog against `libs/contracts/src/living-scene/technique.ts`. (08 §19)
        **Verify:** new
        `libs/yemaya/living-scenes-runtime/src/compose-assist/technique-catalog-parity.spec.ts`
        — every allowlisted technique id exists in the catalog and the catalog
        parses under the contract schema.
  - [ ] **Q1.08.f** [V1.0] Use or delete the server card routes:
        `GET /v1/living-scenes/card` and `POST /v1/living-scenes/card/cues` have
        no caller while the web card evaluates tier and cue policy in the
        browser. Fetch the card and post cues through the routes, keeping the
        library calls as optimistic UI. (08 §21) **Verify:** new
        `apps/oshun/web/src/components/studio/generation/LivingSceneCard-server-policy.spec.tsx`
        — a cue the server refuses is shown refused even if the client
        evaluation allowed it.
  - [ ] **Q1.08.g** [V1.0] Enforce composition caps on the server: free tier up
        to 6 segments and 90 seconds, paid up to 24 and 8 minutes exist only in
        the client's `validateCompositionDraft`; the keep route takes any score.
        Check them from the session tier in
        `apps/oshun/bff/src/routes/living-scenes.ts`. (08 §22) **Verify:** new
        `apps/oshun/bff/src/routes/living-scenes-tier-caps.spec.ts` — a free
        member keeping a 7-segment score answers 403 with the cap.
  - [ ] **Q1.08.h** [V1.0] Wire determinism as a CI gate: run `checkDeterminism`
        over the real render path once T14.a lands. 2026-09-18: depends on
        T14.a; N-node parity, continuity evals and the 30-fixture corpora stay
        later-release. (08 §23) **Verify:** new
        `apps/oshun/bff/src/routes/living-scenes-determinism.spec.ts` — a
        living-scenes spec fails when two renders of one score differ.
        `blocked:upstream`
  - [ ] **Q1.08.i** [V1.0] Stop shipping buttons that do nothing: the public
        viewer renders cue verbs as handler-less `<button>`s
        (`apps/oshun/web/src/app/scene/[id]/page.tsx:357`). Render them as a
        read-only list of cues used. (08 §25) **Verify:** an axe run through
        `apps/oshun/web/e2e-inspect/` (chromium, `workers=1`) on a share page
        reports zero serious or critical violations and no inert button.
  - [ ] **Q1.08.j** [V1.0] Join the four islands into one journey: compose
        promotion emits a `score-schema` Score, the card's Keep posts it, the
        offerings list links to share creation, and the share links to the
        viewer, inside the signed-in shell. 2026-09-18: depends on T14.a and P9.
        (08 §26) **Verify:** new
        `apps/oshun/web/e2e/living-scene-journey.spec.ts` (chromium,
        `workers=1`) — one e2e spec walks compose, keep, share and view for a
        signed-in member. `blocked:upstream`
  - [ ] **Q1.08.k** [later] Give the AAA Scene Score Editor a store:
        `apps/yemaya/studio-web/src/score-editor/ScoreEditorPage.tsx` runs on
        one fixture score and never calls `/v1/score-editor`. Load and save
        scores through the Yemaya API and keep the fixture as the seeded
        example. (08 §20) **Verify:** new
        `apps/yemaya/api/src/routes/score-editor-store.spec.ts` — a saved score
        is re-opened with its version incremented.
  - [ ] **Q1.09.a** [V1.0] Engage throttles: every governed step runs with
        `throttle: null`
        (`libs/oshun/agentic-studio/src/runs/governed-orchestrator.ts:599`).
        Resolve a server-side per-tool and per-tenant throttle config as kill
        switches are resolved, and persist its state between requests. (09 F12)
        **Verify:** extend `apps/oshun/bff/src/agentic/runs-route.test.ts` — the
        call over the bucket answers `throttled` with a retry-after.
  - [ ] **Q1.09.b** [V1.0] Put the cheapest unused run controls to work: expose
        operator pause and cancel over the lifecycle store
        (`@oshun/agentic-studio/runs/run-controls` is already imported at
        `apps/oshun/bff/src/agentic/run-lifecycle-store.ts:33`) and accept a
        `mode` on submit that maps to a budget envelope once T4.b lands; list
        the engines still unused in the route header. (09 F13) **Verify:**
        extend `apps/oshun/bff/src/agentic/run-lifecycle-store.test.ts` — pause
        then cancel reach their states with audit events.
  - [ ] **Q1.09.c** [V1.0] Give the operator console its actions:
        `apps/oshun/admin/src/components/AgenticOperationsPanel.tsx` can only
        arm kill switches. Add per-run pause, cancel and mark-reviewed, filters
        by family, tenant and approval state, and a per-run drill-down that
        shows the now-populated tool calls. 2026-09-18: uses the routes of
        Q1.09.b. (09 F14) **Verify:** new
        `apps/oshun/admin/src/components/AgenticOperationsPanel-actions.spec.tsx`
        — a run row opens its tool-call transcript; pause calls the route.
  - [ ] **Q1.09.d** [V1.0] Close the one customer agent surface's loop: after
        T4.a, add a completion notification and a run-history read to
        `apps/oshun/web/src/components/lilith/AreteReviewDraftRun.tsx`, and
        record the wider invocation matrix as a later release in its header.
        2026-09-18: depends on T4.a. (09 F15) **Verify:** new
        `apps/oshun/web/src/components/lilith/AreteReviewDraftRun-history.spec.tsx`
        — a completed run shows its draft and appears in history.
        `blocked:upstream`
  - [ ] **Q1.10.a** [V1.0] Retire the second, unused audit-integrity engine and
        correct the comment that points at it: the live store already seals
        every event into an HMAC hash chain and verifies it on read (see the
        note above), while `verifyAuditChain`, `applyRedactions` and
        `buildCorrelationThread` in
        `libs/oshun/tenant-console/src/audit-explorer/audit-explorer.ts` still
        have no app caller, and
        `apps/oshun/bff/src/admin/admin-audit-events-store.ts:13-15` still
        describes a firehose feed into that explorer which does not exist.
        Either mount the library's redaction on the tenant-scoped audit read
        (`apps/oshun/bff/src/routes/tenant-console-reads.ts`) or mark the module
        deprecated with a dated note, and fix the comment. (10 §10) **Verify:**
        new `apps/oshun/bff/src/admin/admin-audit-chain.spec.ts` —
        `grep -rn "firehose" apps/oshun/bff/src/admin/admin-audit-events-store.ts`
        prints nothing, and the library module either has a BFF importer with a
        spec or carries the dated deprecation.
  - [ ] **Q1.10.b** [V1.0] Remove the weak fallback from the secret generator:
        `generateRawToken` mints API keys and webhook signing secrets and falls
        back to `Math.random()` under a "non-secret" annotation
        (`apps/oshun/bff/src/admin/admin-integrations-registry-store.ts:160-165`).
        Use `randomBytes(24).toString('base64url')` or throw, and fix the
        annotation. (10 §13) **Verify:**
        `grep -n "Math.random" apps/oshun/bff/src/admin/admin-integrations-registry-store.ts`
        prints nothing.
  - [ ] **Q1.10.c** [V1.0] Wire the two tenant-admin demo pages whose backends
        exist: `apps/oshun/tenant-admin/src/app/status/page.tsx` over
        `GET /v1/status` and `help/page.tsx` over `GET /v1/communications/help`,
        dropping their demo label. (10 §12) **Verify:** new
        `apps/oshun/tenant-admin/src/app/status/status-live.spec.tsx` and its
        `help` twin — each renders the mocked BFF payload and an honest
        unavailable state.
  - [ ] **Q1.10.d** [V1.0] Add `GET /v1/tenant-console/integrations`, filtering
        the integrations registry snapshot to the session tenant in the style of
        `apps/oshun/bff/src/routes/tenant-console-reads.ts`, and render it on
        the tenant-admin integrations page. (10 §12) **Verify:** new
        `apps/oshun/bff/src/routes/tenant-console-integrations.spec.ts` — tenant
        A never sees tenant B's keys or webhooks.
  - [ ] **Q1.10.e** [V1.0] Stop the tenant cockpit blaming the network for a
        permission error: `tenantBffGet` returns `null` for any non-ok response
        (`apps/oshun/tenant-admin/src/lib/tenant-bff.ts:44-46`), so a 403 reads
        as "backend unreachable". Return the status and render a scope-mismatch
        message; link the cockpit from the operator surfaces. (10 §14)
        **Verify:** new `apps/oshun/tenant-admin/src/lib/tenant-bff.spec.ts` — a
        403 renders the permission message, a network error renders the
        unreachable one.
  - [ ] **Q1.11.a** [V1.0] Call the residency guard: `enforceResidency` in
        `apps/oshun/bff/src/middleware/residency-guard.ts` has no caller. Invoke
        it from the memory, export and deletion route families its header lists,
        with the deployment zone. (11 §16) **Verify:** extend
        `apps/oshun/bff/src/middleware/residency-guard.spec.ts` — an export
        request for an out-of-zone artifact answers 403 and writes the audit
        event.
  - [ ] **Q1.11.b** [V1.0] Remove the second retention table: enforcement exists
        (see the note above), so `retentionDaysFor` in
        `libs/oshun/privacy/src/export-deletion/deletion.ts`, which still has no
        app consumer, is now a rival source of truth for the same numbers.
        Delete it or make it read `OSHUN_RETENTION_RULES` from
        `libs/contracts/src/common/data-retention-rules.ts`. (11 §17)
        **Verify:** new
        `apps/oshun/bff/src/privacy/retention-sweep-worker.spec.ts` with a fixed
        clock — `grep -rn "retentionDaysFor" libs apps` shows no independent
        table of day counts; a spec asserts the two agree if the function
        survives.
  - [ ] **Q1.11.c** [V1.0] Keep one consent taxonomy:
        `libs/oshun/privacy/src/consent/consent.ts` defines a second state
        machine beside the live `@oshun/contracts` plus
        `apps/oshun/bff/src/consent/state.ts`. Collapse the duplicate, and for
        the unconsumed compliance, breach and DSAR-intake modules either mount a
        route or mark them deferred in their headers. (11 §18) **Verify:**
        `grep -rn transitionConsent libs apps` shows one implementation.
  - [ ] **Q1.11.d** [V1.0] Make `/profile/data` the privacy center: the real
        consent ledger, a link to memory controls, exports and deletions, and
        the residency display on one page; redirect `/profile/notifications` to
        the settings panel. 2026-09-18: depends on T15.a. (11 §20) **Verify:**
        an e2e-inspect walk — every privacy control is reachable from
        `/profile/data` by keyboard. `blocked:upstream`
  - [ ] **Q1.11.e** [V1.0] Give the four governance consoles one severity
        vocabulary and cross-links: adopt P0–P3 from `@oshun/trust-safety`
        across the admin workspaces, the moderation console, the abuse inbox
        (`apps/oshun/bff/src/routes/admin-abuse-reports.ts`, S1–S3 today) and
        takedowns, and link items by subject user and content id. (11 §22)
        **Verify:** new `libs/contracts/src/governance-severity.spec.ts` — all
        four payload schemas share the severity enum; an item view links to the
        same subject in the other stores.
  - [ ] **Q1.12.a** [V1.0] Cascade a Telegram unlink: `unlinkTelegram`
        (`apps/oshun/bff/src/telegram/user-state-store.ts:957`) marks the link
        revoked and nothing else. Invalidate the member's Mini App sessions and
        purge their scheduled Telegram reminders. (12 §9) **Verify:** new
        `apps/oshun/bff/src/telegram/unlink-cascade.spec.ts` — after unlink a
        previously minted Mini App token is refused and no Telegram reminder is
        dispatched.
  - [ ] **Q1.12.b** [V1.0] Ingest email bounces and complaints:
        `ingestEmailWebhookEvent`
        (`libs/oshun/messaging-channels/src/email/index.ts`) has no route. Add
        the provider event webhook and check the suppression list in
        `deliverDispatchedMessage`. (12 §10) **Verify:** new
        `apps/oshun/bff/src/routes/email-events-webhook.spec.ts` — a hard bounce
        suppresses the next send to that address.
  - [ ] **Q1.12.c** [V1.0] Record deliverability: a per-member delivery record
        exists for the transparency route
        (`apps/oshun/bff/src/channel-ops/message-delivery-store.ts`), but no
        delivery result feeds `computeChannelDeliverability`
        (`libs/oshun/messaging-channels/src/deliverability.ts`). Aggregate real
        `MessageDeliveryResult`s per channel and show the summary on the admin
        messaging console. (12 §10) **Verify:** new
        `apps/oshun/bff/src/reminders/deliverability-recording.spec.ts` — two
        sends and one failure yield the expected per-channel rate.
  - [ ] **Q1.12.d** [V1.0] Build the digest or remove its settings: members can
        set digest frequency and delivery time, and nothing produces one. The
        engine file the report named
        (`apps/oshun/bff/src/notifications/digest.ts`) is gone, and
        `apps/oshun/bff/src/channel-ops/digest-inputs-store.ts` honestly reads
        an empty projection with a do-not-send verdict. Compose the projection
        from the owning stores and send by email on a worker tick, or remove the
        digest controls. (12 §10) **Verify:** new
        `apps/oshun/bff/src/notifications/digest-worker.spec.ts` with a fixed
        clock and Mailpit (`:1025`/`:8025`) — a daily digest for a member with
        two unread items arrives once; or the settings UI has no digest section.
  - [ ] **Q1.12.f** [V1.0] Stop the settings promising more than is delivered:
        in `apps/oshun/web/src/components/profile/NotificationPreferences.tsx`
        mark toggles the send path does not enforce as not yet active, and
        remove each mark as P2.b and Q1.12.d land. (12 §11) **Verify:** new
        `apps/oshun/web/src/components/profile/__tests__/NotificationPreferences-inactive-marks.spec.tsx`
        — an unenforced toggle carries the mark; the mark list is driven by one
        exported constant.
  - [ ] **Q1.13.a** [V1.0] Turn request tracing on in the BFF: the canonical
        package exists (`libs/shared/tracing`, a real OpenTelemetry
        `NodeTracerProvider` at `src/tracer.ts:405`) and the BFF already imports
        it for Eve trace context in the assistant and workbench paths, but
        nothing constructs the tracer at boot, requests get correlation-id
        logging only (`libs/shared/bff-kit/src/tracing.ts`), and
        `infra/hetzner/docker-compose.yml` has no OTLP endpoint. Construct the
        tracer in `apps/oshun/bff/src/server.ts` behind an env-gated OTLP
        exporter, add a server span per request, and propagate `traceparent` to
        the domain adapters. This is the build task behind the "confirm
        OpenTelemetry" items of `docs/releases/v1/specs/todos-p2.md` §55. (13
        F10) **Verify:** new `apps/oshun/bff/src/__tests__/otel-tracing.spec.ts`
        with an in-memory exporter — one request produces a server span and a
        child span for the adapter call sharing a trace id.
  - [ ] **Q1.13.b** [V1.0] Prune or wire the unused telemetry modules: 47 of the
        modules in `apps/oshun/web/src/analytics/` had no importer on
        2026-06-11. Re-count, delete those still unused with their tests, and
        add one admin analytics view over `GET /v1/admin/analytics/events`. (13
        F11) **Verify:** new
        `apps/oshun/web/src/analytics/no-orphan-telemetry.spec.ts` asserts every
        non-test module in the directory has an importer outside it; the admin
        view renders mocked facets.
  - [ ] **Q1.13.c** [V1.0] Make a feature flag gate something, or stop fetching
        flags on every navigation: no component calls `useFeatureFlag`. Wire
        `homeLayoutV2` or `domainSwitcher` as the first real gate, or defer the
        bootstrap until a consumer exists. (13 F12) **Verify:** new
        `apps/oshun/web/src/experimentation/first-flag-gate.spec.tsx` — flipping
        the flag changes the rendered Home layout; or the network panel shows no
        flag fetch per navigation.
  - [ ] **Q1.13.d** [V1.0] Close `/metrics`: `apps/oshun/bff/src/app.ts:1253`
        serves route names, volumes and error rates to anonymous callers.
        Require the observability admin scope or a static bearer from
        `OSHUN_METRICS_TOKEN`, closed by default in production. (13 F13)
        **Verify:** new `apps/oshun/bff/src/__tests__/metrics-auth.spec.ts` —
        anonymous 401, correct bearer 200.
  - [ ] **Q1.13.e** [V1.0] Stop anonymous floods evicting real telemetry:
        `POST /v1/analytics/events` prunes one shared ring of 5000
        (`apps/oshun/bff/src/analytics/events-ingest.ts:38,79`). Partition
        retention by origin, or limit by event volume rather than request count.
        (13 F14) **Verify:** extend
        `apps/oshun/bff/src/analytics/events-ingest.test.ts` — 6000 anonymous
        events leave a signed-in member's 10 events readable.

## DEPLOY register additions (provisioning, not code)

Store-listing URLs (smart-app-banner targets malformed/dead) · metis adapter
downstream service · channel creds as before. See per-report DEPLOY sections.

---

## Execution order

S1–S12 strictly first (S1 unblocks honest Tara/Arete data for H5/H6). Then H
(bright-line honesty), then T (structural; T1 early — it converts dozens of
degraded surfaces), then P, then Q. UX cohesion items inside H/T tasks should be
fixed with the task that touches the surface.
