# V1 Product-Coherence Audit — 2026-06-12

**Method.** Fresh-eyes product walkthrough of the live stack (real signup →
cookie consent → 10-step onboarding → home → all six domain hubs → explore /
activity / library / search / profile / notifications / assistant), driven
through a real browser against the dev web app (`:3010`) + BFF (`:4010`) +
docker infra, with every console error and failed request triaged to root cause,
followed by code-level verification and fixes. This document is the running
artifact for the standing V1-polish goal; the spec baseline is `V1/features.md`
(V1 Exit Criteria §"v1-exit-criteria").

## What holds up (verified by use)

- **First-run experience is genuinely strong.** The welcome page has a
  distinctive editorial identity ("One shell. Six domains. A calmer way in."),
  trust commitments up front, a privacy-respecting cookie banner, and the
  10-step onboarding presents 8 well-written outcome cards with domain pairings
  and a skip affordance.
- **Tara is visibly the product center** (exit criterion #2): home leads with
  "Begin from center", one clear sequence instead of competing dashboards, and
  the domain grid presents Tara as the spine with five companions.
- **The domain rooms are real product, not stubs**: Tara (guided sit course,
  teachers rail, live class booking), Nyx (real ephemeris for tonight's date,
  interactive chart, Nisaba cross-link), Nisaba (full sutta reading desk with
  concept cards), Metis (cross-domain courses "with Nyx"/"with Nisaba",
  grounded-and-disclosed AI tutor), Arete (humane cold start).
- **Honest-absence design is a real practice**: Veritas's method note ("the
  surface stays empty rather than showing unverified content"), Arete's "No
  habits yet", assistant outage notices.
- The backlog formally agrees: `V1/TODOS.md` is 1,640/1,641 checked (only
  §23.1.1 crypto-payments Aje bridge open).

## Fixed this session (all committed + pushed to main)

1. **Member crisis-frame record** (`/profile/safety`, web + mobile) — was a
   fabricated 'acute-grief' incident for a fictional member; now fetches the
   member's real durable frames via new `GET /v1/safety/crisis-frames/mine`
   (honest empty/unavailable states; owner-only; 14 tests).
2. **Assistant surface** — first message raced session creation and posted to
   literal session id `local` (404 → false outage notice on a healthy service);
   keyword-triggered "tool action" cards animated random progress to a
   fabricated "complete"; failed history fetch rendered four invented sessions.
   All three fixed (await in-flight session; fabrications removed; honest
   history outage disclosure).
3. **Veritas home briefing** degraded a downstream outage as an unhandled 500 on
   every home load; now the endpoint's fail-closed 503 (regression test).
4. **Nyx chart fabrication** — chart showed a fixed `MOON · 84%` + invented
   LEO/JUPITER while the headline showed the real illumination (12%); the
   tonight card now exposes structured moon state from the same ephemeris and
   the chart renders a phase-accurate lit region bound to it.
5. **Metis draft isolation (privacy)** — course proposals were a global store:
   any member's draft rendered as "drafted by you" on every other member's
   schoolroom. Now owner-scoped end-to-end (postgres column + index, store
   filter, member-required POST) with an isolation regression test.
6. **Metis first-run hero** — honest-empty resume cursor rendered as "Resume at
   § 0 · week 0 of 0"; now a first-run invitation.
7. **Arete coach card** — always claimed "Two adjustments offered" with
   Approve/decline buttons that wrote only to localStorage (nothing read it
   back), even for members the engine offered nothing. Now renders the recovery
   plan's real offered action only when one exists, and the decision is a
   durable per-member BFF record (`POST /v1/arete/coach/decision`,
   snapshot-persisted, undo-able).
8. **Activity timeline honesty rework** — `/v1/activity` emitted fabricated rows
   for every member ("Reset session completed", invented provenance
   bundles/citation counts, two hardcoded continuity threads, invented assistant
   excerpts), even on adapter failure. Now serves only the member's real history
   (saved continuations with the adapter's own titles/timestamps, logged
   observations, habit records, sitting completions); outages are disclosed in
   the partial-failure envelope with no stand-in rows. Stale e2e specs that
   asserted the fabricated rows were rewritten (honest empty state + client
   analytics against a contract-shaped stand-in).
9. **One consumer brand** (owner decision) — domain hubs were branded "Lilith"
   (wordmark, "Ask Lilith", operator-bar default) vs OSHUN everywhere else;
   consumer surfaces now carry OSHUN / "Ask OSHUN", coach persona speaks as
   "your coach". Internal design-system naming unchanged.
10. **Original teacher personas** (owner decision) — Tara shipped real
    meditation teachers (Sam Harris, Tara Brach, J. Goldstein, Pema Chödrön) as
    personas with session attributions; replaced across BFF/web/fixtures/ tests
    with original personas (Saro Imani, Tessa Marin, Jonas Kael, Pia Halvorsen).
    Attributed quote-of-the-day citations remain as quotations.
11. **Misc.** — "1 threads are ready" pluralization + zero state; stale
    `AdminInboxDecisionPanel` and `TaraSitPlayer` unit tests realigned with the
    real (server-confirmed / disclosure-semantics) behavior.

## Open items (prioritized)

### P1 — product coherence / correctness

- **Dual-nav schism** _(RESOLVED by owner decision, 2026-06-12)_: the owner
  chose "keep the hub manuscript top-nav, add an explicit way back" — the hub
  top-nav is the intentional reading register. Implemented:
  `data-lilith-nav-home` "← Home" (aria-label "Back to OSHUN home") in the
  customer nav, live-verified on /veritas, /tara, and /nyx.
- **Domain-service dev orchestration gap** _(CLOSED 2026-06-12)_: Tara, Arete,
  and Veritas run as real HTTP services via
  `scripts/dev/run-v1-domain-services.mjs` (databases + migrations; fixed the
  Arete service's nanoid-into-uuid ids and missing bridge-user resolution). Nyx,
  Nisaba, and Metis adapters now run **in-process** by default — matching
  V1/ARCHITECTURE.md's BFF-routed-domain-library model — over the real
  ephemeris + a real-coordinate celestial catalog (nyx), the public-domain
  corpus + member reading record (nisaba), and the curated catalog + durable
  tutor-session store (metis), each with domain-correctness tests (35 across the
  three) and `OSHUN_<D>_ADAPTER=http` escape hatches. The home Nyx perspective
  and Arete practice modules and the Nisaba daily passage now serve 200/live
  end-to-end. **Veritas** — the last true HTTP downstream — now has its
  `/v1/oshun/*` facade
  (apps/veritas/api/src/interfaces/http/routes/v1/oshun-facade.ts): all 11 read
  paths + saves/follows verified live against the real Postgres schema (67-table
  migration applied, taxonomy seeded: 10 categories, 16 topics, 5 sources),
  Tara-style bridge users in `auth_users`, verdicts passed through the
  `FactCheckOverallStatus` enum verbatim, source credibility from recorded
  assessments or honestly `unknown`. Fixed along the way: two never-ran boot
  bugs in `createPoolFromEnv` (`.getPool()` fused onto `parseInt`;
  `connectionString` silently ignored → SASL failure), the seed script's bare
  `PrismaClient` constructor, and the readiness probe conflating "Elasticsearch
  intentionally not deployed" with "failing" (tri-state `not_configured` check;
  BFF availability now reports `serviceStatus: ok` instead of a permanent false
  "degraded"). Article feeds stay honest-empty until real editorial content is
  ingested — that ingestion (content ops) is the remaining product gap, not
  plumbing.
- **`useInsertionEffect must not schedule updates`** _(FIXED 2026-06-12)_: the
  stack-trap repro showed the culprit was `useBrowserSearchParams`'s history
  patch, not `PwaBootstrap` — Next's `HistoryUpdater` calls
  `history.replaceState` inside a `useInsertionEffect`, and our patched method
  synchronously dispatched `oshun:locationchange`, whose listeners `setState`d
  inside that window. The dispatch now defers one microtask and coalesces
  same-tick bursts. Verified: signup→onboarding repro, 0 errors. Fixing it
  surfaced a worse product bug, also fixed: `/signup` (and `/signin`, `/login`,
  `/register`, `/recover`) were never routes — the proxy treated them as
  _protected_ paths, bouncing anonymous visitors to
  `/welcome?redirect=%2Fsignup` so the auth-entry page itself rode along as the
  post-onboarding destination (a 404 for a brand-new member). They are now
  proper 301 aliases to `/welcome?mode=…`, and `sanitizeRedirectPath` rejects
  auth entry pages as post-auth targets (callers fall back to their defaults).
  Verified live: all five aliases 301 correctly, protected-path and legacy
  redirects unchanged, signup now lands on clean `/onboarding`.
- **Veritas hub actions with no lead** _(VERIFIED FIXED 2026-06-12)_: the
  `leadAvailable !== false` gate in `rooms.tsx` hides "Save to notebook / Read
  sources / Share excerpt" in the honest-empty state. Live browser check: 0
  action buttons, honest "no grounded lead" copy renders.

### P2 — polish / honesty residue

- **Voice waveform** _(FIXED 2026-06-12)_: the assistant's listening waveform
  now measures the real microphone signal — a parallel `getUserMedia` capture
  feeds an `AnalyserNode` (fftSize 64, lower-spectrum bins averaged into 12 bars
  at 100ms cadence); the `webRandomUnitInterval` fabrication is gone. When
  capture is unavailable (no getUserMedia / permission denied / no AudioContext)
  the waveform honestly hides while recognition keeps running. Capture is
  released on stop, recognition end, and unmount (tracks stopped, context closed
  — also fixes a pre-existing interval leak when recognition ended on its own).
  Tests assert exact measured bar heights from a deterministic analyser (would
  fail on random levels) + honest absence.
- **`/arete/review` & weekly letter** _(RESOLVED via explicit label,
  2026-06-12)_: the H5 depth-page register now covers this —
  `AreteDepthPreviewBanner` mounts `DomainPreviewBanner` on every fixture-backed
  `/arete/*` depth page (only habits/offerings/weekly are live and unlabeled).
  Verified in-browser: both `/arete/coaching` and `/arete/review` render the
  "illustrative example data, not your live account activity" notice. The
  data-driven branch stays blocked honestly: no journal store exists anywhere in
  the system (the BFF room discloses `journalText: ''` for the same reason), so
  a "real" letter has no source data until journal capture ships — a feature
  build, not a polish item.
- **Activity simulation module cleanup** _(DONE 2026-06-12)_: types, lookup
  tables, and the two live formatters extracted to `activity-models.ts`;
  `activity-simulation.ts` deleted with its dead fixture blocks
  (`TIMELINE_ITEMS`, `MILESTONES`, `ACHIEVEMENTS`, `STREAK_CALENDAR`,
  `WEEKLY_DIGEST`, `groupByTime`, `formatDate` — verified zero consumers). All
  four importers repointed; 90/90 activity tests pass.
- **Activity filter empty copy** _(FIXED 2026-06-12)_: the empty state now
  distinguishes `no-history` ("No activity yet — sessions, readings, and
  observations will appear here as you use OSHUN.") from `filters` (the old
  copy), via `data-activity-empty-reason`. Live-verified: a fresh account
  renders the no-history register.
- **Quote-of-the-day attributions** _(RESOLVED 2026-06-12)_: legal exposure
  removed rather than flagged. The attributed daily pool in
  `TaraCollections.tsx` (Thich Nhat Hanh, Eckhart Tolle, David Lynch, Ma Jaya
  Sati Bhagavati, Hermann Hesse) is now five verifiable public-domain quotations
  (Marcus Aurelius/Long, Thoreau, Tao Te Ching/Legge, Pascal/ Trotter, Whitman).
  The ~23 UNattributed program-day epigraphs — many recognizable copyrighted
  lines (Thich Nhat Hanh ×5, Dalai Lama, Krishnamurti, Anne Lamott, Ram Dass,
  living authors) — are replaced with original product copy in the same
  editorial voice; the genuinely public-domain lines (Shakespeare, Meister
  Eckhart, John Lubbock) stay. Consistent with the confirmed "original personas
  over real teachers" decision.
- **Service-worker scope** _(VERIFIED + FIXED 2026-06-12)_: verification
  confirmed the risk was real — the SW caches all `/v1/*` GETs (except
  `/v1/auth`) stale-while-revalidate keyed by URL only (the Cache API ignores
  cookies), and logout never purged them: one member's authenticated responses,
  warmed authenticated HTML, member media, and queued offline mutations would
  persist and serve to the NEXT member on the same browser. Fixed:
  `clearLocalAuthState` now posts `OSHUN_CLEAR_MEMBER_DATA`; the SW
  (CACHE_VERSION v5) purges BFF/static/media caches + the sync queue and
  re-precaches the public shell, keeping only hashed public JS/CSS.
  Live-verified: 10 cached `/v1/*` entries before, zero member-bearing caches
  after, public shell restored. Unit tests cover the client helper's post +
  honest false when uncontrolled.

### P3 — not yet audited (next walkthrough phases)

- **Mobile app walkthrough** (expo) — same product-lens pass as web. _(Static
  pass DONE 2026-06-12)_: tsc clean; jest suite brought from 5 failed / 200
  passed to 205 suites / 1,040 tests green. Found + fixed: (1)
  `formatDomainLabel` had no Metis case, so Metis-linked shared-concept threads
  rendered a duplicate "Nisaba" chip (concurrent graph commit added Metis
  links); (2) five offline-cache suites were stale against the honest download
  manager (b60952a467 made transfers real) — they now drive the manager's REAL
  queue→transfer→verify pipeline through a shared healthy transfer-port seam
  (`download-manager.test-helpers.ts`); (3) native device ids regenerated on
  EVERY call (no persistence — broke push registration dedup); now persisted via
  expo-secure-store (localStorage on web, session-stable in-memory fallback);
  (4) app version was a fabricated '1.0.0' (real manifest says 0.1.0) — now read
  from expo-constants with an honest 'unknown' fallback. Adversarial fabrication
  scan otherwise clean (the notification-route E2E harness's "simulated"
  self-description is its honest register). Remaining: the interactive
  simulator/device walkthrough (deferred — RAM-constrained alongside the dev
  web+BFF stack).
- **Studio (486 routes), operator, tenant console** surfaces. _(Spot-check pass
  2026-06-12, 6 routes + failing-request triage)_: consoles render real lane
  content with zero 5xx, but the triage surfaced — and fixed — THREE systemic
  infrastructure bugs affecting every studio console and every sign-in: (1) all
  ~360 lane consoles fetch their `/v1/admin/studio/*` endpoints with relative
  URLs that 404'd against the Next origin — added the `/v1/:path*` → BFF rewrite
  in next.config (+ `/v1` exempted from the proxy's page-redirect gate so the
  BFF answers its own 401 JSON instead of a 307 to /welcome); consoles now reach
  the BFF and render their designed registers (e.g. honest "admin scope
  required" 403 for members). (2) The api-client token accessor was registered
  in an AuthProvider effect that runs AFTER children's effects, so every sign-in
  fired a volley of unauthenticated profile/preferences/saved-items requests
  (401 noise on every page) — accessor now reads a synchronously-updated ref.
  (3) Repaired found test breakage: pwa-runtime current-path normalization
  (fallout from the redirect-sanitizer hardening) and two isis-provenance
  fixtures broken by a concurrent ledger-validation tightening. Batch 2
  (tenant-isolation, rbac-policy, observability-dashboards, launch-readiness,
  notification-center, /operator): all render real consoles, zero page errors,
  every /v1/admin/studio/\* call reaches the BFF and renders the designed
  member-vs-admin-scope register. Known cosmetic residual: one fail-soft
  `/library/saved-items` 401 fires once around the signup transition (store
  keeps local cache and retries; profile/preferences 401s are gone). Full
  console-by-console product pass across the 486 routes remains open.
- **Assistant conversation quality** _(first pass DONE 2026-06-12)_: probed the
  live reply pipeline end-to-end. Architecture is sound — every turn runs the
  Lilith crisis catalog BEFORE intent routing; the LLM free-text composer exists
  and is correctly fail-closed on the `OSHUN_LLM_*` triple-key gate
  (deploy-bound); replies are grounded in real adapter data. The locally fixable
  gap was vocabulary: natural wellbeing language ("I keep waking up at 3am",
  "I'm so stressed", "my mind is racing") fell through to the "could you
  rephrase" fallback because `tara.start_meditation` knew only meditation
  jargon. Extended with sleep/insomnia/stress/anxiety/racing-mind keywords + 5
  patterns + examples (427/427 lib tests, 8 new). Live-verified the full chain:
  utterance → intent → tara adapter → honest reply at every tier (service down →
  honest outage; empty catalog → "no recommendations right now, browse Tara?";
  never fabricated content). Voice/avatar runtime pass also DONE: voice gates on
  real SpeechRecognition/speechSynthesis presence and the listening waveform now
  measures real mic levels; avatar mode is a capability + entitlement-gated
  state machine (renderer/microphone/ synthetic-voice support statuses, explicit
  fallback, synthetic-voice and synthetic-avatar disclosure indicators,
  provenance + memory scope) with no fabricated embodiment — absence is
  disclosed, never simulated. Remaining for this item: reply depth needs the LLM
  composer creds (deploy-bound).
- **Cross-device handoff, offline/PWA behavior, notifications delivery.**
  _(Email delivery VERIFIED end-to-end 2026-06-12)_: the verification-email
  sender is honest fail-closed (null without OSHUN_MESSAGING_EMAIL_FROM +
  OSHUN_WEB_BASE_URL + a transport), and dev never configured it — Mailpit
  shipped in the compose file but had received zero emails ever. With the four
  keys in the root `.env` (now created; documented in `.env.example`), the full
  loop passes: signup → real SMTP → Mailpit → token from the actual email →
  `/auth/verify-email` page in a real browser → confirm → `emailVerified: true`
  on the next login. Offline/PWA behavior was verified earlier today (SW
  member-data purge; mobile offline caches now tested through the real download
  pipeline). Cross-device handoff pass DONE (2026-06-12): the home multi-device
  continuity panel advertised watch complications and wearable glance cues in
  the present tense — no OSHUN watch surface exists (V1/features.md lists it as
  platform-dependent scope; the only wearable app is Iris, health-domain) —
  those cards are replaced with the REAL capability (profile/preferences/library
  sync across signed-in surfaces) next to the live sync-status signal,
  browser-verified. The pass also surfaced a real continuity bug: the
  theme-continuity strategy/keyword maps were keyed on the pre-canonicalization
  theme vocabulary, so guidance silently never fired for canonical themes
  ('breath', 'presence', …) — rewritten onto the canonical 15-theme vocabulary,
  and 12 stale home-suite tests (old theme values + one fabricated-IEA-briefing
  heading expectation) realigned to honest behavior; home suite 31 files / 257
  tests green. Remaining: push-notification delivery (device tokens
  deploy-bound; mobile registration dedup fixed today).
- Remaining exit criteria: admin/tenant operability, agentic studio governance,
  public web/distribution readiness, observability/runbooks. _(Public
  web/distribution spot-check DONE 2026-06-12)_: robots.txt 200 (now also
  disallowing `/v1/`, `/studio`, `/operator`, `/workspace`, `/library` — the
  consoles and the newly same-origin API were crawlable; `/studio/compose`
  already self-declares noindex), sitemap.xml 200 with 17 public URLs and no
  protected surfaces, manifest.json 200, full OG/twitter meta + rendering
  opengraph-image on /welcome, landing 200. _(Observability/runbooks inventory
  DONE 2026-06-12)_: 15 incident runbooks in docs/runbooks (assistant/
  grounding/provenance failure, privacy incident, provider failover, model/
  persona rollback, backups), 60 studio runbooks under docs/releases/v1/
  runbooks, docs/operations covers SLOs + on-call + alert-routing +
  log-retention, the compose observability profile ships Prometheus/
  Grafana/Jaeger, and the BFF serves real Prometheus metrics at /metrics
  (oshun*bff_requests_total verified live). *(Admin/tenant operability
  walkthrough DONE 2026-06-12)_: all 8 /operator surfaces render with zero page
  errors; six consoles (tenants, incidents, audit, SSO, personas, LMS) read live
  /v1 endpoints and render their designed member-vs-admin-scope registers. The
  four fixture-backed consoles (index, admin inbox, studio review, single-tenant
  view — invented incident counts, a fictional "247 members" roll) rendered
  fabricated operations data with full authority to any signed-in member; they
  now carry an OperatorPreviewBanner register (operator layout, browser-verified
  on fixture vs live segments) until each is wired to its BFF endpoint.
  _(Agentic studio governance verified 2026-06-12)_: the generation studio is
  entitlement-gated at the edge — /studio/isis/_ sits behind the §24.11 boundary
  in proxy.ts (members without the generation tier get the designed hard-block
  404 / upgrade CTA; live-verified as a member), the review-approval and
  launch-readiness governance consoles are live BFF-backed with admin-scope
  registers, the Isis provenance ledger enforces chronological append + checksum
  integrity (tests realigned today), and the paper trail exists (governance ADRs
  0044/0052, docs/compliance/governance-reviews, model/ persona rollback +
  provenance-failure runbooks). With this, every P3 line item in this audit has
  at least a verified first pass; the remaining open work is deploy-bound (push
  tokens, LLM composer creds, content ingestion) or explicitly deferred
  (interactive mobile simulator walkthrough — the expo-web proxy was attempted
  and is non-viable: dev mode crashes inside Expo's own HMR client on a monorepo
  pretty-format version mismatch, and `expo export --platform web` exits 0 while
  emitting no bundle; web is not a V1 mobile surface, so the walkthrough
  genuinely requires the iOS/Android simulator, which this 16 GB host cannot
  safely run alongside the dev stack. Mobile coverage stands on the static pass:
  tsc clean, 205 suites / 1,040 tests, adversarial scan, and the four defects
  fixed today). _(Full studio sweep DONE 2026-06-12)_: all 475 static studio
  routes swept in a real browser against the production build (batches of 50,
  concurrency 1, fresh chromium per batch, memory steady at ~64% free): 387
  rendered with zero page errors / zero 5xx / zero nav failures, and all 88
  /studio/isis/\_ routes returned the designed §24.11 entitlement boundary with
  ZERO leaks. The 11 dynamic [caseId] routes need real case fixtures and were
  recorded as skipped. The sweep also caught a BFF boot breakage (noble-hashes
  v2 subpath in three isis provider files, committed separately).

### Formally open backlog

- `V1/TODOS.md` §23.1.1 — crypto payments (Aje promotion + V1 bridge
  scaffolding): the single unchecked task in the execution backlog.

## Fresh product walkthrough + V1 exit-criteria scorecard (second pass, same day)

**Method.** A second fresh-eyes walkthrough against the PRODUCTION build: cookie
consent ("Essential only") → signup → all 10 onboarding steps clicked through
interactively (exits into the Tara surface) → home → all six hubs → universal
search → library → a /domains power surface → ⌘K command palette — every screen
captured and reviewed visually, then judged against V1/features.md §"V1 Exit
Criteria".

**Found and fixed in this pass:**

1. **Fabricated outage on every member's home** — the admin communications seed
   shipped an ACTIVE "Voice playback is degraded in us-east-1" warning banner +
   open public incident; fictional operational status rendered as fact to every
   member (and contradicted the same seed's all-'operational' components map).
   The seed now carries no active banner and the example incident is
   resolved-at-seed (invisible to members; admin/test fixture retained).
   Live-verified gone.
2. **Real-teacher leftovers** — "Read the passage Sam cites" (Tara hub Nisaba
   cue) and "Sam is saying" (mobile sit player) survived the
   Sam-Harris→Saro-Imani persona replacement. Fixed; live-verified.
3. **Metis dead affordance** — the "Open assessment" card rendered enabled
   Begin/Rubric buttons against "No assessment is open." with em-dash
   placeholder metadata. The model now carries an explicit `open: false` and the
   card renders the honest register ("Assessments open as your course weeks
   complete.") with no actions. Live-verified.

**Consumer exit-criteria verdicts (each observed directly this pass):**

- _One coherent product across six domains_ — ✅ unified OSHUN brand, one design
  language across every hub, consistent top-nav register with "← Home",
  consistent honest-absence registers.
- _Tara visibly the product center_ — ✅ onboarding frames "Tara sets the spine"
  and exits into Tara; home leads with the daypart anchor; Tara hub is the
  richest room (course, teachers, breath ritual).
- _Nisaba and Metis fully integrated, not merely routes_ — ✅ Nisaba is a real
  reading desk (sutta, sections, marginalia, glossary) cross-linked from Tara
  and Nyx; Metis courses are taught "WITH NYX"/"WITH NISABA" and its tutor
  declares grounding + AI disclosure inline.
- _Sophia grounding visible where evidence matters_ — ✅ Veritas lead carries a
  method note explaining the empty surface; the Metis tutor banner reads "AI
  TUTOR · GROUNDED IN 4 SOURCES · DISCLOSED"; Tara's curated rail discloses "BY
  EDITOR + SIGNAL".
- _Cross-domain continuity useful and credible_ — ✅ Tara→Nisaba passage cue,
  Nyx→Nisaba canon link, Metis cross-domain courses, home continuation rails;
  continuity threads tested (home suite 257 green).
- _Degrades safely_ — ✅ honest registers observed on every empty/outage surface
  this pass (Veritas no-lead, Metis first-run, assistant outage notices), and
  the §24.11 entitlement boundary verified across all 88 gated routes.
- _Public web ready_ — ✅ welcome/landing OG complete, robots/sitemap/ manifest
  verified, status page serves only real (now-unfabricated) incidents.

The operator/tenant/studio/agentic criteria carry their evidence in the sections
above (operator consoles, 475-route sweep, governance pass). The two criteria
that remain deploy-bound are unchanged: editorial/audio content ingestion, and
provider credentials (push, LLM composer).
