Disciplines · Audits

V1 Residual Audit — Product Surfaces (features.md 258–449)

mounted with no props.

7sections26 minread

On this page

Date: 2026-06-11 · Auditor slice: V1 Product Promise + Customer Web + Customer Mobile + Shared Consumer Shell + Public Web and Distribution. Method: static source reading + grep only; every delegation chain followed to the bottom; all 06-10 ground-truth fixes (A–E phases) treated as landed and NOT re-reported. Accepted postures (D1 preview register on registry-covered surfaces, C3 guardedFixtureRoute dev-only fixtures, E5/E7 retirements, env-gated fail-closed providers) are excluded per the brief.


P0-HONESTY — fabricated data / fabricated success on live paths#

1. Home "Daily plan" is a permanent unlabeled simulation while a real routines backend sits unused#

Severity: P0-HONESTY Evidence:

  • apps/oshun/web/src/components/home/HomeWorkspace.tsx:496<DailyPlanV2 /> mounted with no props.
  • apps/oshun/web/src/components/routines/DailyPlanV2.tsx:1071-1072const routines = propRoutines ?? SIMULATED_TODAY_ROUTINES; const recommendation = propRecommendation ?? SIMULATED_RECOMMENDATION;
  • DailyPlanV2.tsx:172-260SIMULATED_TODAY_ROUTINES (userId: 'user_sim', frozen todayDate = '2026-05-24', "Quick Morning Start" with completion state) and SIMULATED_RECOMMENDATION.
  • Real backend exists and is reachable: apps/oshun/bff/src/routes/routines.ts:110-407 (GET/POST /v1/routines, templates, recommendations, executions) and typed hooks at apps/oshun/web/src/lib/hooks/use-bff.ts:2226-2271zero callers on home. Spec promise: features.md:318-319 (redesigned dashboard reflecting the V1 home model); features.md:260 (coherent product, not fabrications). What the code does: every member's home shows the same fabricated routine list and progress bar with no fallback/preview disclosure of any kind — not even the "Curated …" pill the other home sections use. This is the C15 fixture pattern, alive on the highest-traffic surface, and the orphaned-engine pattern (tested backend pointed at nothing) at once. Fix sketch: Wire DailyPlanV2 through useUserRoutines()/useActiveRoutineExecution()/useRoutineRecommendations(); honest empty state ("No routines yet — create one") when the member has none; delete SIMULATED_TODAY_ROUTINES from the live path (keep as test fixture only).

2. /profile + /settings fabricate "Domain statistics" and "Connected services"; the Connect/Manage button is dead#

Severity: P0-HONESTY Evidence:

  • apps/oshun/web/src/components/ProfileSettingsPanel.tsx:106-143DEMO_DOMAIN_STATS (Tara 42 sessions/315 min/7-day streak, Veritas 89 sessions, "Top action: Breathwork" …).
  • ProfileSettingsPanel.tsx:157-167DEMO_SERVICES: "Google — Connected · 2 hours ago", "Apple Health — Connected · 30 min ago".
  • ProfileSettingsPanel.tsx:1105 — section subtitle computes "199 total sessions" from the fixture; :1120 renders the fixture grid; :1448 "2 connected"; :1454 fixture list.
  • ProfileSettingsPanel.tsx:1495-1512 — the per-service Connect/Manage <button> has no onClick — a dead control.
  • Mounted live at app/profile/page.tsx:90 and app/settings/page.tsx:35. Spec promise: features.md:401-403 (profile/settings for account, billing entry, memory controls …); features.md:269-270 (make state visible where it affects user trust). What the code does: every user is told they have 199 sessions of cross-domain history and two live OAuth integrations syncing their data. Both claims are fabricated for everyone; the integration buttons do nothing. Fix sketch: Delete both DEMO_ blocks. Domain statistics: derive from the real /v1/activity + achievements reads the Activity surface already uses (or render the honest "no stats yet" note). Connected services: render the real calendar-connections client (profile/calendar-connections-client.ts) plus an honest "no integrations connected" empty state; remove the dead button or wire it.

3. Billing is a client-side simulation: every user seeds as paid "Pro" with a Visa and a paid invoice, and "purchase" fabricates success#

Severity: P0-HONESTY (with a P1 entitlement-coherence edge) Evidence:

  • libs/oshun/auth/src/billing-store.ts:976-1025createDefaultOshunBillingSnapshot() seeds currentPlan: 'pro', accountStatus: 'active', payment method "Visa ending in 4242", and one paid $12.00 invoice dated now.
  • apps/oshun/web/src/profile/store.ts:58-61 — billing store built on localStorage with that seed and no BFF fetcher (compare the consent/export/deletion stores in the same file, which do have fetchers). lib/auth-context.tsx:145 only clear()s it on sign-out — which re-seeds the same fiction (billing-store.ts:929).
  • apps/oshun/web/src/components/profile/BillingSection.tsx:113-135handlePurchaseClickoshunWebBillingStore.purchase(...) (a localStorage mutation; no payment provider, no server call) then reports "<plan> active · $X/mo · renews <date>" and syncProfilePlan(targetTier) (:1472-1474) flips the client profile plan.
  • apps/oshun/web/src/components/billing/ShellFeatureGate.tsx:46-47 — client feature gates resolve entitlements from that client-mutable profile plan.
  • BillingSection.tsx:280-291,651 — "Report failed charge" button calls simulateBillingFailure() which injects a fabricated decline into the local store instead of reporting anything.
  • Same seeds on mobile: apps/oshun/mobile/src/profile/store.ts:21-41 (in-memory storage, same createDefaultOshunBillingSnapshot), consumed by app/(tabs)/profile.tsx via getLatestBillingInvoice/getDefaultPaymentMethod.
  • Two sources of truth: the real plan lives server-side in customerAuthStateStore (A6 entitlement middleware; C10 settlement flips it) and is never reconciled into this surface. Spec promise: features.md:401-402 (billing entry in profile/settings); features.md:260 (coherent product). What the code does: a free user opens Billing and sees an active paid Pro subscription, a card on file, and a paid invoice — none real. Clicking "purchase" fabricates a successful subscription without moving money or touching the server plan, then unlocks client-side feature gates. The crypto path (quote→settle, C10) is real and entirely separate — the two billing stories contradict each other. Fix sketch: Give the billing store a BFF fetcher like its siblings: GET /v1/billing deriving plan from the session's persisted plan (A6) + real invoices from the C10 invoice store; seed honest-empty (free, no methods, no invoices). Purchase buttons route into the real payment flow (crypto quote) or an honest not_configured state. Delete simulateBillingFailure or rename/rewire it to a real support intake.

4. Fabricated profile-stats seed (7-day streak, 18 saved items, 5 active domains) reaches Home and Profile for every signed-in member#

Severity: P0-HONESTY Evidence:

  • libs/oshun/auth/src/profile-store.ts:240-258createDefaultOshunProfile defaults stats: { streakDays: 7, savedItems: 18, activeDomains: 5 }, plan: 'pro', verified: true.
  • apps/oshun/web/src/lib/auth-context.tsx:530-545syncProfileStore takes identity/plan from the real session but passes stats: undefined for a new user → fabricated defaults persist.
  • Rendered: components/home/HeroBanner.tsx:457,1003 ("{streakDays} day practice streak"), components/ProfileSettingsPanel.tsx:307-309,950 ("Day streak" stat tile).
  • Mobile mirror: apps/oshun/mobile/src/auth/mobile-auth-context.tsx:223-235 — same pattern, plus fallbacks plan ?? 'pro', verified ?? true.
  • Only savedItems is ever reconciled to reality (lib/library/webLibraryStore.ts:270-287); streakDays/activeDomains have no real writer anywhere. Spec promise: features.md:318-319 (home reflects the V1 home model); the C2/C3 ground-truth fixes made the Arete streak real — this is a second, contradicting streak. What the code does: a brand-new member's hero banner congratulates them on a 7-day practice streak they don't have; the same fake number sits beside the now-real Arete streak from /v1/arete/streak — two sources of truth, one fabricated. Fix sketch: Seed stats at honest zero; derive streak from the real /v1/arete/streak (or the achievements stats adapters) and activeDomains from real domain activity; never default plan to 'pro' or verified to true when the session doesn't say so.

5. Web notifications center fabricates notifications whenever the live feed is empty — including a fake billing alarm#

Severity: P0-HONESTY Evidence:

  • apps/oshun/web/src/components/NotificationsCenterPanel.tsx:1036-1038setItems(mapped.length > 0 ? mapped : buildInitialNotifications()) on success-but-empty, and the same fixture in the catch.
  • NotificationsCenterPanel.tsx:207-260buildInitialNotifications(): "Cross-domain handoff is ready", and "Billing review needs your confirmation — Support flagged a renewal mismatch on your Veritas upgrade" with a deep link into the billing upgrade flow. Spec promise: features.md:399-400 (notification center behavior for mixed-domain, admin, support … events); features.md:428-429 (honest loading/empty/degraded states). What the code does: a new user with zero notifications is shown fabricated unread items by design (not just on outage), one of which is a fake billing problem that funnels them toward the (also fake — finding 3) upgrade surface. No disclosure. Fix sketch: Empty feed → honest empty state ("You're all caught up"). On fetch failure → disclosed unavailable state. Delete buildInitialNotifications from the live path.

6. Home ActivityFeed serves fabricated first-person activity on outage#

Severity: P0-HONESTY Evidence: apps/oshun/web/src/components/home/ActivityFeed.tsx:76-110FALLBACK_ACTIVITY: "Completed morning breath sequence" (24 min ago), "Saved 'AI regulation frameworks' to reading queue", "Logged ISS visibility observation", "Completed daily check-in"; served at :465, :494, :526 whenever /v1/activity errors or returns no items array, with only a small notice ("Home kept its fallback entries"). Spec promise: features.md:395-396 (cross-domain activity timelines); features.md:284-285 (degrade safely); features.md:428-429. What the code does: exactly the silent-fixture pattern C15 deleted from /activity — the home strip was missed. The outage notice exists but the content remains first-person fabricated history with live "x min ago" timestamps. Fix sketch: Replace the fixture fallback with the same honest unavailable presentation /activity now uses (empty list + CustomerShellOutageBanner reason). Delete FALLBACK_ACTIVITY.

7. Home mixed-domain recommendations swap in SIMULATED_CROSS_RECOMMENDATIONS on error or empty, with fabricated personal context#

Severity: P0-HONESTY Evidence:

  • apps/oshun/web/src/components/home/MixedDomainRecommendationsLane.tsx:61-62,109isUsingFallback = isError || !hasLiveRecommendationPayload; then recommendations={isUsingFallback ? SIMULATED_CROSS_RECOMMENDATIONS : …}. Disclosure is an 11px "Curated shell mix" pill.
  • components/cross-domain/CrossDomainRecommendations.tsx:124-186 — fixture items claim "The centering session you finished this morning", "Your focus goal for the week", "The breathing reset you just completed" as sourceContext.
  • CrossDomainRecommendations.tsx:944,957-962useDefaultFallback = true by default: ANY consumer passing an empty list silently gets the fixture.
  • The fallback also bypasses selectHomeMixedDomainRecommendationSlate — the spec's suppression rules are not applied to what actually renders. Spec promise: features.md:324 (mixed-domain recommendations with reason labels and suppression rules). What the code does: a new user with genuinely no recommendations (empty payload — not an outage) is told a centering session "you finished this morning" points somewhere. Reason labels on fabricated reasons are worse than no recommendations. Fix sketch: Empty payload → honest empty/cold-start invitation (the cold-start module already exists in this file). Error → disclosed unavailable. Flip useDefaultFallback default to false. Keep the fixture only for the (orphaned) CrossDomainHub or delete it.

8. Mobile Activity tab seeds fabricated notifications and never syncs without a dev auth token#

Severity: P0-HONESTY Evidence:

  • apps/oshun/mobile/app/(tabs)/activity.tsx:41-130INITIAL_NOTIFICATIONS: "Streak milestone reached — seven consecutive recovery sessions", "Weekly pattern detected — your strongest focus block appears between 9:00-11:00 AM", "Clear-sky window starts soon … in your location".
  • :244,252 — rendered as initial state. :422-424syncNotifications returns early if !devAuthToken, so the fixtures stand with no disclosure. :434-437 — on fetch failure the message says "Showing local fallback timeline" but the fallback content is fabricated personal insight. Spec promise: features.md:395-396 + 399-400 (activity timeline, notification center); the C16 fix covered mobile home only. What the code does: the mobile activity timeline fabricates behavioral insights ("your strongest focus block") and location claims for everyone whose session token isn't present, and on error. Fix sketch: Initial state empty + loading; unauthenticated → sign-in prompt; failure → honest unavailable (no fixture timeline). Delete INITIAL_NOTIFICATIONS from the live path.

9. Mobile Profile tab: zero network — fabricated "Renata" Iris memories with working forget/export controls, fabricated linked sessions#

Severity: P0-HONESTY Evidence:

  • apps/oshun/mobile/app/(tabs)/profile.tsx0 hits for fetch(/client./api. in 4,092 lines (verified by grep) except listSessions via the auth context.
  • :376-470INITIAL_MOBILE_PROFILE_MEMORY_ENTRIES: "Renata prefers a short breath session before breakfast…", "Renata is comparing Epictetus and Marcus Aurelius…", with fabricated provenance chains ("consent:routines granted by member", audit chain ids) and recall events. Every setMobileMemoryEntries call (:1560-1755) is a local mutation of this fiction; there is no hydration from /v1/iris/adapter/review (which C4 wired for web).
  • :267-291,663INITIAL_LINKED_SESSIONS ("iPhone 15 Pro", "Chrome on Mac", "Pixel Tablet" in SF/Austin) is the signed-out state (:893-894 resets to it) and the pre-hydration state; the governance fact row reports "Sessions: 3 active" from it (:786-809). Spec promise: features.md:401-403 (memory controls, sessions, devices in profile); features.md:269-270 (memory/consent state visible where it affects trust). C4's whole point was that fabricated memory + pre-granted consent is a trust-surface lie — the mobile mirror was missed. What the code does: mobile members "review", "forget", and "export" memories Iris never stored, about a person who doesn't exist; consent toggles flip local fiction; signed-out users see three fabricated active devices. Fix sketch: Hydrate memory controls from the same /v1/iris/adapter/review + /consents reads the web uses (typed client already has the BFF base); honest empty + ungranted-consent start. Linked sessions: empty + sign-in prompt when unauthenticated; on error show the error, never the fixture.

10. Mobile Library tab still ships the OLD fabricated Iris handoff (C16 fixed home only)#

Severity: P0-HONESTY Evidence:

  • apps/oshun/mobile/app/(tabs)/library.tsx:15,36buildMobileHomeIrisHandoffState() (no args) feeds MobileIrisHandoffCard.
  • src/navigation/mobileIrisHandoff.ts:15-35,61-110MOBILE_IRIS_CONTINUITY fabricates memory counts (coreBlockCount: 4, workingCount: 7, archivalCount: 18 …), and the builder fabricates items: "Bhagavad Gita 2.47 — 64% progress", "Evidence reflection study path — 68%", issuedAt hardcoded '2026-05-06T10:15:00.000Z'.
  • Contrast: app/(tabs)/index.tsx + src/network/oshun-bff-client.ts:499-511 fetch/consume the REAL durable /v1/iris/mobile-handoff channel. Spec promise: features.md:339-341 + 411-412 (hand-off and continuity between desktop and mobile). What the code does: the library tab tells every member a desktop reading session is waiting to resume at 64%. Two sources of truth for the same fact (real channel on home, fiction on library), differing within one app. Fix sketch: Point the library tab's card at the same fetched handoff state home uses (lift the fetch into a shared hook); honest "no handoff waiting" when null. Delete buildMobileHomeIrisHandoffState's fixture path.

Severity: P0-HONESTY (plus orphaned-engine) Evidence:

  • apps/oshun/mobile/src/share/mobileUniversalSharing.ts:41-60buildMobileUniversalShareModel() defaults to buildDefaultMobileUniversalShareRequests(); summary renders "N/M universal links active".
  • :63-130 — fabricated requests: an active library share created 60s ago, an invite to learner@example.edu with fabricated rate-limit usage counters.
  • Rendered on the live library tab: app/(tabs)/library.tsx:49 (buildMobileUniversalShareModel() — no real requests passed).
  • libs/oshun/shell-core/src/universal-sharing.ts (buildConsumerShellShareCenter) has zero runtime consumers besides this fixture wrapper (repo-wide grep) — the real per-link permission/expiry/revocation engine the spec describes is orphaned, while real sharing exists separately in apps/oshun/bff/src/routes/library-sharing.ts. Spec promise: features.md:406-407 (universal sharing, invite, referral flows with per-link permissions, attribution, expiry, revocation). What the code does: tells the member they have live outstanding share links and a pending email invite; "manage" affordances operate on fiction. Meanwhile the real library-share records (BFF) are a second, disconnected truth. Fix sketch: Feed buildMobileUniversalShareModel from the real library-sharing reads (GET /v1/library/...share family) or render the honest empty share center; delete the default fixture requests. Decide the shell-core engine's fate explicitly (wire it over the BFF share store or record an E5-style retirement note).

12. Mobile calendar sync is a simulation end-to-end: fabricated provider connections, static transport, success haptic on a no-op "Sync now"#

Severity: P0-HONESTY Evidence:

  • apps/oshun/mobile/app/(tabs)/index.tsx:821-823 — home card state = buildMobileCalendarSyncState({ issuedAt }), no real inputs.
  • src/calendar/mobileCalendarSync.ts:164-220buildDefaultMobileCalendarSyncItems() fabricates scheduled rituals/reminders with stale May-2026 dates; :321-346 builds fabricated Google/Apple/Outlook connector connections and inbound-change/conflict counts; :588-602createStaticCalendarTransport is a test double on the default live path (upsertEvent echoes the event back as "synced").
  • app/(tabs)/index.tsx:1081-1094 — "Sync now" fires triggerHapticIntent('success') and navigates to an activity filter; no calendar API is called. Repo-wide grep: no expo-calendar, no provider OAuth anywhere in mobile.
  • Contrast: web calendar connections are wired to real BFF routes (apps/oshun/web/src/profile/calendar-connections-client.ts:67-85routes/calendar-connect.ts, calendar-sync.ts). Spec promise: features.md:371-373 (calendar integrations — Google, Apple, Outlook — with two-way sync where authorized). What the code does: claims three calendar providers are connected and syncing, shows reconciliation counts from a static double, and confirms "sync" with a success haptic without syncing anything. This is fabricated success, not an honest fail-closed seam. Fix sketch: Drive the card from the real BFF calendar-connection reads (same routes web uses); unconnected → honest "Connect a calendar" state; "Sync now" calls the real sync route or is removed. The deterministic reconciliation engine can stay — behind real connector/transport inputs only.

13. Mobile capture: "Request permission" fabricates an OS permission grant; no camera/mic/photo capture exists#

Severity: P0-HONESTY Evidence:

  • apps/oshun/mobile/app/(tabs)/index.tsx:1126-1130handleRequestCapturePermission = grantMobileCapturePermission(current, permission) (a local state flip) + success haptic; :1132-1143 — tapping a not-ready capture action also just flips the permission to granted locally.
  • src/capture/mobileCaptureIntake.ts — pure model, no expo-camera/expo-image-picker/expo-av; repo grep shows the only real requestPermissionsAsync in mobile is notifications (src/notifications/registration.ts:209). Spec promise: features.md:374-376 (camera, microphone, photo-library capture with explicit permissions and per-domain redaction policies). What the code does: displays "granted" for OS permissions the OS was never asked for, then routes "capture" actions to ordinary screens — no capture pipeline exists behind the card. Fix sketch: Either wire real expo permission requests + a minimal capture→upload leg (the avatar upload asset-store seam from B13 is reusable), or remove the capture card from home and keep the redaction-policy model for V1.x. Never render a granted state the OS didn't return.

P0-STRUCT — structural breaks#

14. 54 standalone /domains/* deep pages bypass the D1 preview banner entirely — including every example the spec names#

Severity: P0-STRUCT (explicitly requested check: surfaces not covered by DOMAIN_PREVIEW_SURFACES) Evidence:

  • The banner mounts in exactly one place: apps/oshun/web/src/components/DomainRouteExperience.tsx:579, used only by the catch-all app/domains/[domainId]/page.tsx:91.
  • 54 concrete pages exist beside it (find app/domains -mindepth 2 -name page.tsx): nyx/star-chart, veritas/fact-check, arete/journal, tara/teachers, veritas/claims/[claimId], nyx/events/[eventId], … Each mounts its component directly (e.g. app/domains/nyx/star-chart/page.tsxNyxStarChart; app/domains/veritas/fact-check/page.tsxVeritasFactChecker) with no DomainPreviewBanner and no shared app/domains/layout.tsx (the only layouts are nyx/renderer and nyx/widgets, unrelated).
  • Those components are exactly the simulation surfaces D1 labeled for: components/domains/nyx/NyxStarChart.tsx:19 imports nyx-simulation-data; components/domains/veritas/VeritasFactChecker.tsx:52 imports veritas-simulation-data; 18 components under components/domains import *-simulation-data (repo grep). Spec promise: features.md:303-309 names /domains/arete/journal, /domains/veritas/fact-check, /domains/nyx/star-chart, /domains/tara/teachers as the power-user layer — all four are unbannered standalone pages. What the code does: the registry says tara/veritas/nyx/arete/nisaba are "in preview", but the preview register renders only on the catch-all room; deep links and in-hub entry points land on the concrete pages, where fabricated user data/journalism/scholarship again carries full visual authority — the precise failure D1 was accepted to fix. Fix sketch: Add app/domains/layout.tsx that resolves the domain segment from the path and renders <DomainPreviewBanner domainId={…}/> above children (one file covers all 54 pages and future ones); keep the registry as the single switch.

15. Veritas/Nyx/Metis/Nisaba /v1/oshun/* facades were never built — home/feed stages for 4 of 6 domains fail by construction#

Severity: P0-STRUCT Evidence:

  • apps/oshun/bff/src/adapters/domain-service-adapters.ts:1095-1380+ — veritas/nyx/metis/nisaba adapters dial /v1/oshun/briefs/trending, /v1/oshun/events/nightly, /v1/oshun/courses/..., /v1/oshun/saved … against {veritas,nyx,metis,nisaba}BaseUrl.
  • Repo-wide grep: no /v1/oshun route and no oshun-facade module under apps/veritas, apps/nyx, apps/metis; Nisaba has no service at all. Only Tara (apps/tara/api/src/oshun-facade/) and Arete (apps/arete/api/src/routes/oshun-facade.ts) were built (C1: "Veritas/nyx repeat = follow-up of the same pattern" — the follow-up never happened).
  • Consumers: apps/oshun/bff/src/routes/feed-helpers.ts:113-200/v1/home (web home + C16 mobile home feed) and the feed/continue/favorites stages call app.domainAdapters.veritas.getTrendingBriefs, nyx.getNightlyEvents, metis.getRecommendedCourses, nisaba.getDailyPassage per request. Spec promise: features.md:265-266 (continuity across web/mobile/home), 364-365 (mobile continuation cards for all six domains), 393-394 (Tara-led home ordering, blended exploration). What the code does: even with every dev service running, domainStatus for veritas/nyx/metis/nisaba in /v1/home is permanently degraded — the partial-failure envelope is honest, but four of six domains can never appear in the home/continue/favorites feed. The "blended" home is structurally Tara+Arete-only. Fix sketch: Repeat the C1 facade pattern for veritas + nyx (both have real repositories), bridge nisaba's adapter to the in-BFF nisaba store it already mirrors (B6/E2 did this for daily-passage on another path — getDailyPassage should read the same store), and metis to its existing /api/* service; contract round-trip tests per C1.

P1#

16. Home Arete practice section fabricates streaks/progress on fallback, labeled only "Curated practice thread"#

Severity: P1 Evidence: apps/oshun/web/src/components/home/homeAretePracticeModels.ts:131-217FALLBACK_HOME_PRACTICE: goals with streakDays: 12/9/17, progress: 68/54/73, continuation "Morning commitment loop — 58%", userId: 'demo-user'; used whenever input.practice == null (loading or error) via :217. Disclosure pill at HomeAretePracticeSection.tsx:267-271 reads "Curated practice thread" — which does not say the streak numbers aren't yours. Spec promise: features.md:325-326 (Arete weekly review home module); the C2/C3 work made Arete streaks real precisely so numbers like these would be honest. What the code does: on any /v1/arete/practice/home failure (and during load on slow links) the member sees specific fabricated streak/progress numbers beside the real streak elsewhere. Contrast the Veritas module in the same directory (homeVeritasBriefingModels.ts:110-126), which was rewritten to an explicit no-fabrication notice — the pattern exists; Arete (and Nyx, below) weren't given it. Fix sketch: Mirror the Veritas fix: fallback carries stage copy + "practice data unavailable" with zero numeric claims; delete the fixture goals.

17. Home Nyx perspective fallback fabricates "Jupiter at opposition" tonight + a 42% observation continuation#

Severity: P1 Evidence: apps/oshun/web/src/components/home/homeNyxPerspectiveModels.ts:95-140FALLBACK_HOME_PERSPECTIVE: "Jupiter at opposition … reaches its closest point to Earth this year", windowStart/peakTime frozen at 2026-03-27, magnitude: -2.8, plus continuation "Track Jupiter through midnight — 42%, lastOpenedAt …". Label on fallback: "Curated perspective" (HomeNyxPerspectiveSection.tsx:238). Spec promise: features.md:325-326 (Nyx events home module); the B-phase ephemeris honesty work (real Meeus engine) exists exactly so sky claims are true. What the code does: on outage, asserts a specific astronomical event is happening tonight (it is dated ten weeks ago) and that the member is mid-observation. Fabricated fact + fabricated personal continuation. Fix sketch: Honest unavailable notice (no event claim, no continuation); the real BFF route is ephemeris-backed, so the fallback should claim nothing.

18. KNOWN RESIDUAL CONFIRMED — workspace→mobile handoff packet still fabricates the Iris continuity header (and more)#

Severity: P1 Evidence: apps/oshun/web/src/components/workspace/irisMobileWorkspaceHandoff.ts:45-68WORKSPACE_IRIS_CONTINUITY: static coreBlockCount: 4, workingCount: 7, archivalCount: 18, episodicCount: 5, semanticCount: 11, static userId: 'oshun-v1-workspace-user', consentState: 'granted' (fabricated consent — C4 made consent real and default-ungranted), lastUpdatedAt: '2026-05-06'. Additionally :141-163 fabricate a voice_session (sessionId: voice-${tab.id}) and an assistant_transcript (conversationId: assistant-${tab.id}) that don't exist. Spec promise: features.md:339-341 (resumable assistant transcripts, hand-off between desktop and mobile); features.md:269-270 (memory/consent state visible truthfully). What the code does: the C16 transport is real (PUT to /v1/iris/mobile-handoff), but the payload's memory counts, consent state, and two of six items are fiction — mobile renders them as truth on resume. Fix sketch: Fetch /v1/iris/adapter/continuity (and the real consent read) to fill the header; include the voice/transcript items only when a real session/conversation id exists; drop the static userId for the session user.

19. Mobile Explore search runs on a hardcoded in-file catalog, never /v1/search#

Severity: P1 Evidence: apps/oshun/mobile/app/(tabs)/explore.tsx:217-585SEARCH_INDEX (in-file items with fabricated freshness publishedAtMs: NOW_MS - 2h and popularityScores); :586 TRENDING_QUERIES (fabricated "trending"); :1339-1340 all search/filtering runs over this constant. Zero network calls in the 3,065-line file (grep client.|api.|fetch( = 0). Web explore meanwhile uses live /v1/search (components/explore/ExploreDashboard.tsx:1621), including the member's real saved items/notebooks (C7). Spec promise: features.md:327-329 (universal search across … notebooks, collections, courses …); features.md:367-369 (mobile command surface for search and jump-to-anything). What the code does: mobile members search a fabricated catalog that can never contain their notebooks/saved items, with fabricated trending and recency — a second source of truth diverging from web search. Fix sketch: Point the explore search box at GET /v1/search through the existing typed BFF client (auth + domain filters already supported); keep the curated collections rows as labeled editorial content; derive "trending" from the analytics ingest (C11) or drop the label.

20. Assistant panel: silent fixture session history + a fake tool-action progress bar#

Severity: P1 Evidence:

  • apps/oshun/web/src/components/assistant/AssistantPanel.tsx:1884-1887loadSessionHistory catch → setSessionHistory(SIMULATED_SESSION_HISTORY); :651-664 four fabricated sessions with topics ("Evening meditation routine", "Weekly goal review").
  • :1545-1578 — when the message mentions "session"/"check-in", the panel renders "Starting … session..." / "Running daily check-in..." with a random-increment setInterval progress bar that always reaches status: 'complete' — no action is performed; the message is simply sent afterwards. Spec promise: features.md:320-323 (assistant dock with persistent context handoff); features.md:339-341 (resumable assistant transcripts). What the code does: outage shows fabricated past conversations as the member's; the progress theater claims an action ran and completed when nothing did (C9/C14 made real runs and replies possible — this UI predates them). Fix sketch: History failure → honest "history unavailable"; delete the keyword-triggered fake tool action (real suggested-actions already navigate/queue real work).

21. E6 cross-device workspace sync is write-only: nothing ever reads the durable slot back#

Severity: P1 Evidence: apps/oshun/web/src/components/workspace/MultiPanelWorkspace.tsx:463pushClientState('workspace-layout', …) on every tab change. Repo-wide grep: pullClientState and mergeByUpdatedAt (lib/client-state-sync.ts:33,50+) have zero callers. Tab restore still reads only localStorage (MultiPanelWorkspace.tsx:392). The other three allow-listed slots (onboarding-progress, recent-searches, study-plan-drafts) have neither push nor pull (recent searches remain pure localStorage — UniversalSearchPanel.tsx:103-114). Spec promise: features.md:337-338 (multi-tab support with per-tab continuity, stable deep-link state); features.md:411-412 (cross-device continuity). What the code does: "another device resumes the same tabs" (the E6 claim) cannot happen — the server copy is written and never read. Stored state never consulted on the hot path. Fix sketch: On workspace mount (signed-in), pullClientState('workspace-layout')mergeByUpdatedAt with the local tabs before first render of restored tabs; adopt the same 6-line pattern for recent-searches in UniversalSearchPanel.

22. "Report failed charge" injects a fabricated payment failure into the member's own billing state#

Severity: P1 (subsumed by finding 3's rebuild, listed separately because the control is actively harmful) Evidence: apps/oshun/web/src/components/profile/BillingSection.tsx:280-291simulateBillingFailure()recordPaymentFailure({ reason: 'Card was declined by issuer.' }) → notice "Payment issue detected. Complete recovery actions to keep access active."; wired to the customer-facing "Report failed charge" button (:651). Spec promise: features.md:401-402 (billing entry); features.md:428-429 (honest states). What the code does: a customer trying to report a real billing problem instead creates a fake declined-card state and is told their access is at risk. The "Retry payment now" recovery then "completes" against the same fiction. Fix sketch: Route the button to the real feedback/support intake (B14's POST /v1/feedback exists) with a billing category; delete simulateBillingFailure.


P2#

23. "What's New" feed is a stale hand-coded changelog that contradicts the product#

Severity: P2 Evidence: apps/oshun/web/src/components/WhatsNewDropdown.tsx:32-86CHANGELOG frozen at v0.8–0.9 / Feb 2026, entries like "Dark theme polish" (the shipped shell is the cream Lilith system per E7). No fetch (grep: 0 network calls). Spec promise: features.md:416-417 (in-app changelog, "what's new" feed). What the code does: serves four-month-old, partially false product education; meanwhile C8 built a real authored-communications store with localization that could carry this feed. Fix sketch: Serve entries from the C8 communications store (new changelog audience/kind) or at minimum refresh the static list and remove false entries.

24. Account switcher computes "last active" against a frozen clock#

Severity: P2 Evidence: apps/oshun/web/src/components/ShellLayout.tsx:225ACCOUNT_SWITCHER_NOW_MS = Date.parse('2026-05-07T12:00:00.000Z'); fed to buildConsumerShellAccountSwitcher at :814. E8 made the identities real but left the frozen now, so relative recency labels/ordering are computed against May 7 forever. Spec promise: features.md:413-414 (multi-account switcher with explicit identity indicator). Fix sketch: Use Date.now() captured client-side post-mount (the file already uses that pattern for shellMountedAtMsRef).

25. Orphaned fixture dashboards still exported beside the live home: KpiGrid (hardcoded sessions = 28, seeded fake sparklines) and CrossDomainHub#

Severity: P2 (orphans — no live consumer found; flagged so they don't get re-mounted) Evidence: apps/oshun/web/src/components/home/KpiGrid.tsx:523const sessions = 28;; :104-112 generateSparkData invents 14-day trends; only its skeleton is consumed (Skeleton.tsx:92, ShellSurfaceStatePreview.tsx). components/cross-domain/CrossDomainHub.tsx:108-125 renders SIMULATED_RITUALS/SIMULATED_CROSS_RECOMMENDATIONS/SIMULATED_CORRELATIONS/SIMULATED_ACHIEVEMENTS; zero consumers (repo grep). Spec promise: features.md:318-319 (the dashboard reflects real model state). Fix sketch: Delete both (and SIMULATED_* exports they anchor), or wire KpiGrid to the real profile/activity stats if it's meant to return.

26. Mobile profile/preferences/privacy stores are memory-only — every setting resets on app restart#

Severity: P2 Evidence: apps/oshun/mobile/src/profile/store.ts:21createMemoryKeyValueStorage() backs ALL mobile stores (profile, preferences, privacy, data-rights, billing), unlike web's createWebStorageAdapter(localStorage). Notification topics, personalization, privacy toggles configured in the profile tab do not survive a relaunch (no AsyncStorage adapter anywhere in the file). Spec promise: features.md:401-405 (profile/settings incl. notifications, personalization preferences); features.md:265-266 (continuity). Fix sketch: Provide an AsyncStorage/SecureStore-backed KeyValueStorage adapter (the auth context already uses secure storage for tokens — same seam).


UX / polish#

27. Voice-input amplitude bars are random noise, not mic levels#

Severity: UX Evidence: apps/oshun/web/src/components/assistant/AssistantPanel.tsx:1811-1815setInterval fills 12 bars with webRandomUnitInterval() while real SpeechRecognition runs. Spec promise: features.md:320-321 (assistant dock quality bar). Fix sketch: Drive bars from a AnalyserNode over getUserMedia (or replace with an indeterminate "listening" pulse that doesn't imply level metering).

28. Metis home fallback says "Resume course" for a course the member never started#

Severity: UX Evidence: apps/oshun/web/src/components/home/homeMetisStudyContinuationModels.ts:47-55FALLBACK_PRIMARY CTA "Resume course" → /courses/crs-002 with progressLabel: 'Course path ready' when there is no continuation. Spec promise: features.md:325-326 (Metis study continuation module). Fix sketch: Cold-start copy should invite ("Start a course path"), not imply an in-progress resume.

29. Adjacent-register inconsistency: home domain modules use three different degraded vocabularies#

Severity: UX Evidence: On the same home screen, a degraded Veritas module says "briefing is unavailable … no graded claim is shown" (honest, homeVeritasBriefingModels.ts:110-126); a degraded Arete module says "Curated practice thread" over fabricated streaks (finding 16); a degraded Nyx module says "Curated perspective" over a fabricated event (finding 17); the recommendations lane says "Curated shell mix" (finding 7). "Curated" is doing dishonest work in three of the four. Spec promise: features.md:428-429 (consistent loading/degraded states); features.md:260 (coherent product). Fix sketch: One shared degraded-module presentation (the Veritas pattern) across all six home modules; reserve "Curated" for genuinely editorial content that makes no personal/factual claims.


DEPLOY#

30. Smart-app-banner store URLs point at listings that don't exist (and the Apple URL pattern is malformed)#

Severity: DEPLOY (store submission is a recorded deploy item) — with a small P2 code edge Evidence: apps/oshun/web/src/lib/mobile-store-links.ts:1-3https://apps.apple.com/app/oshun (App Store URLs require /app/<name>/id<digits>; this 404s structurally, not just pre-submission) and the Play URL for an unsubmitted package; SmartAppBanner.tsx:151,161 sends users there. Spec promise: features.md:443-445 (store submission assets, listing content); features.md:430+ (public-web-to-app conversion funnel). Fix sketch: Env-gate the banner (OSHUN_APP_STORE_URL/OSHUN_PLAY_STORE_URL unset → suppress the store CTA, keep the PWA install path), fixing the malformed Apple pattern when the real id exists.


Severity count#

Severity Count
P0-SEC 0
P0-HONESTY 13
P0-STRUCT 2
P1 7
P2 4
UX 3
DEPLOY 1
Total 30

Notes for the fixer: findings 3+4 (auth-client seeds) and 16+17+29 (home fallback register) each share one root fix. Finding 14 is a one-file fix with outsized honesty coverage (54 routes). Finding 15 is the largest build item (two facades + two bridges) and is the C1 follow-up the audit already named.