V1 Web PWA · Conventions

Journey Walkthrough Results — Oshun V1 PWA

Conventions and reading guide for the V1 Web PWA walkthrough.

13sections35 minread1table

On this page

Per-journey Playwright walk results. Every current V1 PWA journey has a matching <journey-slug>.md file under this directory. Additional dated sweep and quality reports remain alongside those 58 journey results.

This page is a cumulative record. Counts inside dated sections describe that specific run; the Journey result ledger and Summary below reconcile the current 58-journey set. The generated Docs Center journey registry uses these conservative ledger markers for status and the matched result files for dates and evidence links.

Sweep reports#

  • playwright-flow-sweep-2026-06-02.md — executes the repo's behavioural E2E suite (601 spec files) against a live prod build + BFF. Non-studio: 320✓ / 345✗ / 2 skip; studio: systemic placeholder-session redirect (root-caused, not real bugs). Real findings: WCAG color-contrast violations, 3 duplicate-DOM defects, and, at the time, a missing user-flag-to-review customer affordance (superseded by the 2026-06-24 moderation read-back addendum). Stricter, behavioural complement to the render-level 2026-05-29 re-walk below.

2026-05-29 comprehensive re-walk (Claude Opus 4.8)#

All 53 journeys re-walked with Playwright against a fresh Next.js 16.2.6 production build (next start -p 3010) and the BFF in mock-adapter mode (USE_MOCK_ADAPTERS=true, the canonical local-dev domain-data path — without it the home-widget endpoints 503/500 because the real adapters fetch downstream microservices that don't exist as runnable apps here). Auth was driven via the real /api/auth/signup path on localhost:3010 (the dev seed endpoint is disabled under next start's NODE_ENV=production).

Headline: the web app works. Every customer/studio/operator surface renders with a proper single <h1>, every domain hub + sub-route resolves (no broken in-app links after the fixes below), the cross-domain bridges are present, and every BFF-backed view loads real (mock) data — the transient client 401s self-heal via the api-client's token-refresh retry. The prior walk's blocked/failed verdicts were all from before the goal-2/3/4 rebuilds.

Bugs found and fixed this pass (frontend):

  • useBrowserSearchParams read window.location.search in its state initializer → React hydration text mismatch (error 418) on every query-param-driven view (e.g. /onboarding?step=memory, 8 errors across the onboarding deep-links). Fixed: start empty on server + first client render, sync in the effect.
  • Home "Begin daily breath ritual" (lilith/rooms) and the 5 ActivityFeed fallback rows linked to non-existent /domains/<x>/<leaf> routes (404). Repointed to the canonical buildHydratedWebDomainRoutePath / hub form.
  • /aaa-upgrade "Continue in Yemaya Studio" and the lilith-studio access-denied "Request operator access" CTAs pointed at dead routes (/welcome/yemaya, /profile/operator-access, both 404). Repointed to /studio and /profile.
  • Hardened two SSR-unsafe render-time new Date() usages on /profile (ProfileSettingsPanel relative time, PreferencesSection date/time previews).

/profile hydration mismatch — FIXED. The residual was a useSyncExternalStore server-snapshot bug: all eight profile stores passed the live client getSnapshot as their getServerSnapshot, so by hydration time the client store held the real user (the auth/profile fetch had populated it) while the server rendered the seed default — e.g. the avatar initials hydrated as "HW" on the client vs "AO" on the server. Fixed in profile/store.ts by giving each store a stable seed snapshot (captured once at module load) as its getServerSnapshot, so client hydration matches the SSR seed and then updates to real data post-mount. Verified gone across multiple runs on the prod build; real profile data still renders. (Two render-time new Date() usages on /profile were also hardened earlier — ProfileSettingsPanel relative time + PreferencesSection date previews.) See agentic-pipeline-customer-invocation.md.

Still needs real downstream systems / external choices (not frontend bugs): crypto settlement, IdP SAML/OIDC, real audio/generation runtimes, live Telegram bot, agent orchestrator, SCIM — surfaced as reachable stubs. See external-dependencies/. Per-journey files below carry the verdict + evidence for each of the 53.

Follow-up issue-fix pass (deeper root-cause fixes)#

A second pass fixed the genuine issues behind the few remaining non-clean lines:

  • Catch-all Cache-Control overriding auth no-store (next.config.mjs). A source: '/:path*' rule applied public, max-age=0, s-maxage=3600, stale-while-revalidate=86400 to every route — including /api/auth/* and gated pages — overriding the auth handlers' no-store. That let a shared CDN cache per-user auth/session responses (s-maxage) and the browser serve a stale anonymous //welcome redirect (SWR), bouncing freshly signed-in users back to /welcome on navigation (the real cause of the first-time-anonymous-visitor Step-9 "bounce"). Removed the blanket Cache-Control (kept CSP + HSTS); static assets keep their explicit rules. Verified: /api/auth/sessionno-store, /welcomeprivate,no-store, and first-time-anonymous-visitor now passes 10/10 across repeated runs.
  • Refresh-token rotation race (bff/src/auth/customer-auth-store.ts). Concurrent refreshes (multi-tab / retried / nav-interrupted) re-presented the just-rotated token and tripped reuse-detection, revoking the whole session. Added a rotation grace window (OSHUN_AUTH_REFRESH_ROTATION_GRACE_MS, default 30s): a just-rotated token is accepted while the session is live; genuine later reuse still revokes (security preserved). Verified: 5 concurrent refreshes keep the session alive; after-grace reuse revokes (unit tests).
  • Sophia answer field-name (bff/src/routes/domain-stubs.ts) — the endpoint now accepts q / question / query and echoes whichever is sent.
  • Walker URL corrections — the placeholder-id and route-group-notation scripts now navigate real routes, so the sweep reflects the app, not the test.
  • Home telemetry "gap" withdrawn — home is instrumented via 52 data-home-continuation-* actions + HomeContinuationTelemetryBridge; the prior "Bug #7" was an attribute-name mismatch, not a missing-analytics bug.

Final full-suite re-run (confirmation sweep)#

After the fixes above, the full suite of 52 journey scripts was re-run end-to-end against the prod build + mock-adapter BFF. Result:

Metric Count
Journeys swept 52
Clean pass (renders, 0 persistent fails, 0 errors) 50
Step failures 0
Page errors (hydration / uncaught JS) — whole suite 0
Product regressions 0

The 2 remaining non-clean lines are verified non-bugs:

  • install-as-pwa {401,503} — auth-mount race during the journey's own mid-walk signup (home-data fetches fire before auth settles, then self-heal); all flagged endpoints return 200 with a valid token (confirmed).
  • public-scene-abuse-report POST 404 — needs a seeded share (test-data gap); the endpoint correctly 404s an unknown shortCode.

The /profile hydration mismatch is confirmed fixed (0 page errors across the sweep + dedicated prod runs).

Run context#

  • Branch: oshun-lt-2-hetzner
  • Worktree: /mnt/volume300/workspace/oshun-lt-2
  • Web app: http://127.0.0.1:3010 (Next prod build, next start -p 3010)
  • BFF: http://127.0.0.1:4010 (tsx watch src/server.ts)
  • Infra: docker compose -f docker/docker-compose.dev.yml up -d
  • Driver: Playwright chromium (headless), driven ad-hoc per journey
  • Screenshots: /tmp/claude-oshun-lt-2-screenshots/<journey>/<step>.png
  • Logs: /tmp/claude-oshun-lt-2-logs/{web,bff,build}.log

Status legend#

  • [ ] — not yet attempted
  • [~] — partially walked (blocked mid-flow, see notes in file)
  • [x] — walked end-to-end, all steps verified passing
  • [F] — failed (the journey itself does not work; bug captured in file)
  • [B] — blocked by prerequisite (e.g., feature not implemented, env missing)
  • [s] — skipped (out of scope for this pass; reason in file)

Journey result ledger (58)#

Anonymous + onboarding (6)#

  • [~] first-time-anonymous-visitor — real Mailpit verify-link flow now covers signup-browser and fresh-browser session handoff with HttpOnly cookies; email-verify-roundtrip also covers safe next, unsafe fallback, and a 44 px Continue target on the verify-email landing page. Residual gaps are OG/Twitter image handlers, ?reauth=1, and onboarding-abandon resume.
  • [~] install-as-pware-walked in goal-4: SW now registers (scope /, installing: true then active after a beat), manifest serves 200 with theme_color: #f1ebdd (Lilith cream, was navy), sw.js serves 200. The four runtime caches (oshun-static-v4, oshun-media-v4, oshun-bff-v4, oshun-runtime-v4) materialize on first visit. CC-BUG-C is confirmed fixed. Residual: the relaunch-cookie nav refresh still doesn't fire on every navigation; needs a follow-up to call cookies.set after router.replace rather than only on first paint.
  • [~] scene-public-viewer-anonymous — tombstone+embed verified after fixing P0 'use server' bug in unlock-actions.ts; password/playable branches need a seeded share. The former double | OSHUN title suffix is fixed.
  • [~] public-scene-abuse-report — anonymous report form posts to the real BFF, live reports surface in the operator inbox, admin resolve/dismiss verbs persist, and the operator takedown cascade is covered at the real-BFF level; remaining gaps are the public-report detail UI action surface, actor restriction, brigade clustering, and signed-in reporter status.
  • [~] sign-up-and-pay-crypto/billing added in 71e53603dd; /v1/payments/methods, /v1/payments/invoices, /v1/payments/crypto/quote, and /v1/entitlements/aaa were added in 03d45f0ad7. Real BFF-issued BTC and USDC/Base paywalls now cover disclosure version, trust-gated address reveal, copy/QR/address, issued-paywall support actions, signed settlement webhook, entitlement read-back, stale-address removal, and idempotent webhook re-delivery; external wallet/provider and oracle/receipt legs remain outside local infrastructure.
  • onboarding-ten-steps — deep-links, abandon/resume, skip/edit/revisit, sensitive-context reconciliation, first-run target weighting, online Finish failure, and browser-offline service-worker Background Sync replay are now covered over real dev infra

Tara (3)#

  • [~] first-tara-sit — happy path reaches player; player and both Home and the player expose their proper <h1>; Tara data renders through the BFF mock adapters. Audio completion/reflection remains outside the headless walk.
  • [~] tara-daily-ritual/tara and /arete render with proper <h1> elements and BFF mock-adapter data; the historic heading and endpoint failures are resolved.
  • [~] tara-to-nisaba-handoff — bridge link added in 860a554b4e (Nāgārjuna passage cue on /tara); full sit-completion handoff still depends on the audio-completion flow (not exercisable headless)

Arete (4)#

  • [~] arete-create-habit/arete/habits rebuilt in 3f130c64f1 as a real Lilith phone-shell list with keptToday/totalDaily chip, "the promise" copy, per-habit streak counts, and a "propose a habit" CTA; /arete/habits/new is a real form posting to POST /v1/arete/habits with awaiting-coach-approval accepted state.
  • [~] arete-streak-recovery — humane tone copy verified (5 recovery matches, 0 shaming); /v1/arete/streak BFF endpoint added in 03d45f0ad7
  • [~] arete-living-offering-create/arete/offerings + /atelier/new pages added in 03d45f0ad7; /v1/arete/offerings + /v1/atelier/jobs + /v1/atelier/scenes BFF endpoints added in same commit; 2026-06-26 coverage now drives /arete/offering keep/send through the live BFF and proves the signed-in /arete/offerings gallery reads the member-scoped draft/sent rows back with private intention previews.
  • [~] weekly-review-arete/arete/review renders its own <h1> with Save as private letter / Close the week actions; the cookie banner no longer hijacks the page heading, and /arete/weekly redirects to the canonical review route.

Nyx (3)#

  • [~] nyx-tonight-observation/nyx/tonight rebuilt in 9ba3d0823d with a highlighted lead-event block + observation- order timeline; /nyx/observation is a real client form posting to a new POST /v1/nyx/observations endpoint with observation_target_missing 422 branch.
  • [~] nyx-to-tara-bridge — deep evidence now proves one deterministic fixed-event card → Tara timer → generic Nyx suggestion path. The /nyx hub's conditional Tara slot has no data producer, the card drops event identity from its href, telemetry remains DOM metadata, audio is wired to an unproxied metadata-shaped endpoint, completion is browser-local, and the 200 BFF recommendation route targets only Nisaba or Metis.
  • [~] nyx-event-calendar-sync-reminder — deep evidence now separates the dynamic agenda and whole-feed ICS, fixed rich catalog and single-event ICS, browser/process action-preference mirror, durable assistant/member reminder, Profile provider export, opt-in in-app worker, injected service-worker payload, and observation stores. These are useful shipped seams, but ids do not converge, detail actions do not create durable reminders, exported/worker URLs are invalid, warm taps do not navigate, and the 35 named cases do not form one reminder-to-observation receipt.

Nisaba (2)#

  • [~] nisaba-notebook-capture-and-cite — deep evidence now covers the owner-scoped BFF snapshot and the Studio study service's Nisaba-native Postgres notebooks/cards, anchor validation, rights- gated publication/export, restart continuity, synchronized annotation items, erasure, local notes, six client formats, and fixture exports. It remains partial because the clients/stores do not converge, legacy links ignore ids, local notes and citations are browser-derived, cross-store writes are not atomic, Studio card append does not authorize the target notebook, and the separate Studio records lack an account export/erasure path.
  • [~] nisaba-scholarly-read — deep evidence now separates the real Epictetus search/detail path, the identity-aligned daily Dhammapada room and annotation count, the room's mismatched save id, and the static Marcus Aurelius depth pages. It remains partial because the named cases do not join one passage to an annotation, notebook, plan, reload, or subject-partitioned offline record.

Metis (2)#

  • [~] metis-lesson-completion — 36 Playwright cases combining browser and API evidence deeply cover the dev/test backlog fixture, schoolroom entry, shared authored reader, local quiz and assessment replay, durability-bound Themis/gradebook ledger, tutor specimen, Nisaba links, and a separate acknowledgement-bound tutor-memory read. Course/lesson ids remain unresolved, production backlog is unavailable, and no account-scoped completion, server assessment read-back, tutor delivery, library save, or Home/schoolroom progress join exists.
  • [~] metis-byom-ingest-to-course — deep coverage proves the query-preserving alias, deterministic text outline, honest URL/PDF pending records, durability-bound and privacy-composed ingest/admin records, and an owner-scoped draft proposal. Those remain three independent stored seams: no runtime path joins an ingest job to safety/Sophia approval, a promoted CourseBuild, or downstream lesson/tutor/assessment reads.

Veritas + Sophia (4)#

  • [~] sophia-grounded-answer/sophia rebuilt in 8ad5d6a4df as a real ask-and-answer surface — single textarea, POST to /v1/sophia/answer, witness-anchored result card with citation weight + source kind chips. Real-BFF Playwright now covers grounded, cautious/partial, and no-source abstention envelopes.
  • [~] veritas-evidence-trail/veritas/evidence rebuilt in 8ad5d6a4df as a real two-pane view (drift cases vs retractions) with resolution-state colour coding and cascade-reason chips.
  • [~] veritas-to-nisaba-deeper — bridge link added in 860a554b4e (case-study notebook cue on /veritas evidence rail)
  • [~] veritas-retraction-cascade-customer-view/veritas/retraction renders the soft-correction view; browser coverage now follows Library and Nisaba disclosures while real-BFF coverage proves source retract → durable cascade → Sophia re-grounding. Persisted notebook acknowledgement and remaining customer fan-out stay open.

Atelier / Scene / Lilith Studio (5)#

  • [~] atelier-image-to-library — /atelier five-room hub renders; /atelier/camera-obscura rebuilt in 8ad5d6a4df as a live job queue with running/queued/done/failed state colouring; ATELIER_ JOBS_FIXTURE has two named jobs.
  • [~] scene-keep-and-share — public share, viewer, embed/oEmbed copy, password gates, download-grant integrity, tenant/Lilith gates, idempotent retry, and operator takedown-cascade consequences are now covered against real dev infra; persisted customer share/revoke management UI remains the main shipped-surface gap.
  • [~] lilith-studio-tara-scene-publishre-walked in goal-4: /lilith-studio/scene/new is reachable and POSTs to /v1/lilith-studio/scenes with the authoring-state response. Auth-gating still bounces to /welcome without a real session, but the page itself is no longer a 404. /lilith-studio/tara rebuilt in 8ad5d6a4df as a read-only mirror of /v1/tara/{today,sittings,ritual}; /lilith-studio/scene/new posts to a new POST /v1/lilith-studio/scenes distinct from the user-facing /atelier endpoint (carries pathId + sessionIndex for course-content authoring).
  • [~] atelier-video-render-queue — deep real-dev-infrastructure coverage verifies the authenticated read-only Cinematheque room, twelve-shot storyboard, three-row fixture queue, selected sh 06 state, disabled Cut placeholder, reduced motion, offline-after-load stability, and 390 px layout without horizontal overflow. Live render jobs, queue mutation, and the Cut destination remain product gaps.
  • [~] atelier-motion-to-cinematheque — deep real-dev-infrastructure coverage verifies the authenticated read-only motion room, timeline/take fixtures, disabled draft actions, URL-scoped handoff to Cinematheque, inbound context rendering, reduced motion, offline-after-load stability, and 390 px layout without overflow. Motion authoring and durable project transfer remain product gaps.

Library + Search (2)#

  • [~] library-save-collection-share — /library renders with proper h1; /library/collections rebuilt in 8ad5d6a4df as a real list (LIBRARY_COLLECTIONS_FIXTURE now has two named collections); /library/collections/new is a real form posting to POST /v1/library/collections with seed-item-id parsing.
  • [~] search-explore-deep-read-library-save — /search + /explore both render with proper h1; real BFF /v1/search results drive saved/recent search memory, recent-query suggestions, Nisaba passage click-through into the exact non-daily passage reader (nisaba-passage-speech id/reference/source/title/body + compare/support rails), Veritas claim click-through into the claim-detail evidence workspace, Veritas story click-through into the story timeline workspace, Tara practice click-through into the immersive session player, Nyx sky-event click-through into the canonical event-detail workspace, Metis course click-through into the active study destination preview, and search-result save → saved-items BFF → cache-free /library hydrate → Nisaba resume URL + exact passage workspace; a live-BFF 403 domain_scope_missing now drives the visible fetch-failure error state with stale rows cleared and same-query Retry reissue; the long /search?q=a list now restores Browser Back scroll position after opening the Nisaba deep-read; the search-save → Nisaba in-domain unsave/resave path now proves the saved-items BFF keeps exactly one nisaba:nisaba-passage-speech row. Remaining gaps are broader non-Nisaba save-producer sync and remaining object-template save/resume permutations beyond the clicked destination surfaces.

Assistant / Agents / Memory (5)#

  • [~] agentic-pipeline-customer-invocation/v1/agents + /v1/orchestrator BFF endpoints added in 03d45f0ad7
  • [~] assistant-handoff-context-carry/v1/assistant/context BFF endpoint added in 03d45f0ad7; 2026-06-24 assistant-session-bff now proves route-level handoff sanitization by accepting a customer Veritas context and dropping a mismatched admin artifact before continuity merge; 2026-06-26 extends that real-BFF spec to prove consented profile + notebook Iris recall is returned in response.memoryRecall and folded into the assistant answer text; the browser continuity spec now also verifies the null-artifact /explore handoff renders Artifact: no current artifact instead of an empty or misleading artifact chip, and verifies the /search entity-mismatch branch posts entity: null with artifact.metadata.entitySanitization='entity-shell-mismatch' while the panel shows Entity: removed (shell mismatch) without the dropped admin label; it also verifies Tara→Veritas domain-shortcut navigation depth by preserving origin=explore&stack=tara through the real session-create payload, assistant continuity rail, and return links; it now verifies a live Nisaba notebook handoff stamps artifact.metadata.notebookId, recalls consented profile + notebook Iris memory through the real assistant message route, and renders the mounted transcript's Remembered context: profile / notebook copy with Memory: profile; 2026-06-26 also closes the browser-visible grounding inspect branch by sending a live Veritas assistant message, asserting the real BFF response.grounding envelope, and clicking Inspect evidence into the shared Veritas inspection overlay with source/citation/support-count read-back.
  • [~] memory-edit-pause-forget — page fixed in adae8117dd; /v1/memory BFF endpoint added in 03d45f0ad7
  • [~] crisis-aware-tone-policy/v1/safety/crisis-resources BFF endpoint added in 03d45f0ad7 (returns Befrienders / Crisis Text Line resources)
  • [~] citation-drift-resolution/v1/veritas/drift BFF endpoint added in 03d45f0ad7

Persona / Psyche (2)#

  • [~] persona-voice-avatar-approval-workflow/profile/persona rebuilt in 9ba3d0823d with three voice rows (Warm cream, Evening tea, Bright stone), provider + approval-state chips, inline audio previews; PERSONA_VOICE_FIXTURE now carries activeVoiceId so the active row is highlighted.
  • [~] psyche-tutor-live-session-to-graded-record/metis/session rebuilt in 8ad5d6a4df to pick the highest-priority lesson (assessmentReady wins) as the lead and queue the next two; total estimated minutes in the masthead.

Messaging / Telegram (3)#

  • [~] telegram-bot-assistant-deliverytelegram-webhook-delivery drives the real BFF webhook: secret gate, rate-limit trigger, slash commands, crisis persona swap, grounded real-source answer, honest abstention, fail-closed grounder throw paths, fail-closed voice STT, successful voice-transcript crisis interrupt, Postgres-backed audit-sink persistence, membership and callback branches; /profile/telegram supplies the linked/unlinked management panel, including client-side expired deep-link disablement and refresh recovery.
  • [x] messages-center-deliver-and-unsubscribe — deep real-BFF, live-browser, and focused durability proof covers the awaited dispatch seam, policy gates, restart-safe inbox/follow/read/archive, shell projection, exact-pair suppression, durable delayed work, manual replay, continuity export, and exact-subject erasure. Automatic scheduling, multi-writer arbitration, upstream production, and external provider delivery remain explicitly outside the result.
  • [~] messages-quiet-hours-and-channel-binding — deep control-plane proof covers the four-channel/five-category read-only summary, mutable quiet-hours/domain editor, non-production email/SMS/WhatsApp preview-code lifecycle, Telegram HMAC seam, and durable message-center delay/replay/safety/erasure branches. Required binding and Telegram user-state writes add restart, rollback, exact-subject fencing, export/erasure, and adjacent-user proof; durable device tokens join session export/erasure. The result separates the independently durable master, preference, binding, reminder, message-center, and Telegram snapshots from automatic replay, production missing-config code delivery, identity convergence, push-selected automatic reminder producers, independently supplied manual recipients, the static revoke phrase, and absent profile-bound provider/ cascade evidence.

Resilience / PWA (3)#

  • [~] multi-device-workspace-handoff — deep evidence covers the workspace packet, local capture, per-user BFF mailbox, native Home consumer, responsive web restoration, and independent profile sync. The result remains partial because capture is not delivery acknowledgment, /v1/devices is an unjoined registration inventory, Library uses a fixture card, expiry is unenforced, and mobile return stops at an assistant prompt instead of reaching web.
  • [~] offline-first-time-usere-walked in goal-4: the four runtime caches (oshun-static-v4, oshun-media-v4, oshun-bff-v4, oshun-runtime-v4) materialize on first visit to /welcome, so the offline shell foundation is there. Later real-infra Playwright coverage drives cached shell navigation, false-online /healthz failures, deep-route offline fallback controls, local offline library edits, and the real sw.js IndexedDB background-sync queue replaying an Arete check-in through the live BFF after an offline reload; the PWA update prompt now also renders the pending service-worker queue count and held-write copy, and offline-shell-routes proves auth/session endpoints remain network-only under the real service worker even when matching cache-poison responses exist. The same spec now proves a truly uncached deep route receives a successful real-service-worker document response from the cached shell and hydrates the retry/cached-route fallback controls from that service-worker marker, including the durable Cache Storage target marker used after hydration; it also drives profile Active devices refresh while offline and asserts the exact network-only denial copy, plus fresh/expired Nisaba 24-hour offline BFF cache response behavior under the real service worker. nisaba-split-view now also drives the live lexicon/morphology inspector offline and asserts cached term forms remain visible while the online-only lookup shows "Available when online."
  • pwa-update-flowre-walked in goal-4: <h1> on / is "Good afternoon, there. Recover the shape of the day." (the proper hero greeting, was misattributed to PwaUpdatePrompt copy in earlier walks); PwaUpdatePrompt.tsx uses <h2>, not <h1>, so it cannot hijack a hero heading. Later real-infra Playwright coverage now drives the staged update prompt, apply→controllerchange, two open tabs reloading exactly once while preserving their own URLs, standalone cold-start relaunch with an active worker and no update prompt, an Arete in-flight BFF check-in that refetches as done after refresh, raw service-worker queued-write replay, real sw.js stale-cache purge, queued-write update-prompt note, and reduced-motion spinner behavior. Remaining PWA-update telemetry is now covered by pwa_update_applied for Refresh-now and silent cold-start paths plus pwa_sync_queued for the real pending service-worker queue row.

Account / Identity (2)#

  • [~] aaa-upgrade-entitlement-bounce/v1/entitlements/aaa BFF endpoint added in 03d45f0ad7; /aaa-upgrade page is the redirect-to-Yemaya-Studio surface (intended, not a bug)
  • [~] account-deletion-and-dsar/profile/data rebuilt in 9ba3d0823d with live consents (three rows), prior exports (sha256 + bytesEncoded), pending deletions; pulls from new /v1/data-rights/{consent,exports,deletions} BFF endpoints.

Trust + Safety (2)#

  • [~] user-flag-to-review — flag flow exists on Veritas / Nisaba / Tara surfaces; /v1/user-reports now routes submitted reports into the admin moderation userReports queue and the focused browser E2E reads the live cust-ur-* record back. The same real-BFF spec now covers moderator decision, audit-search read-back, and reporter-scoped receipt updates, then opens /profile/safety to verify the browser-visible submitted report receipt row and /messages to verify the in-app decision notification, Safety Center deep-link follow, and clicked receipt. Actor suspension, second-reviewer signoff, external push/email delivery, appeal, crisis/severity classification, brigade rate-limits, and remaining origin surfaces stay open.
  • [~] incident-triage/operator/incidents rebuilt in 9ba3d0823d with a real two-lane table (open vs recently-closed), severity-coloured chips (p0 red → p3 soft), MONO_L uppercase mitigation state; INCIDENTS_FIXTURE filled with two named incidents.

Tenant admin (7)#

  • [~] tenant-onboard/v1/tenants and POST /v1/tenants/onboard were added in 03d45f0ad7; tenant CRUD UI lives in the separate apps/oshun/tenant-admin application.
  • [~] tenant-bulk-member-invite-scim/operator/tenants rebuilt in 9ba3d0823d with a real roster sorted by memberCount; TENANTS_FIXTURE filled with House + Oxbow Foundation + Redbird Institute (184 + 41 members), plan tier displayed. Real-BFF/Postgres Playwright now covers SCIM Users lifecycle, SCIM Groups membership sync, and SCIM Bulk request sequencing, with SCIM audit events read back through the real admin audit-log API.
  • [~] tenant-bulk-operations-export — dedicated V1 admin bulk operations/export journey added. The real-BFF spec covers platform scope gates, users bulk-operation validate/dry-run/stage/commit with real row-level commit outcomes, entitlements bulk-operation commit into /v1/profile plan read-back, rosters bulk-operation commit into /v1/admin/lms/oneroster read-back, unsupported content commit failure without false committedAt, validation-error blocking, per-operator isolation, content JSON export manifest creation/read/verify, persona-assignment commit into /v1/personas/active, export matrix failures, and audit-search read-back. Tenant/operator upload-preview UI, CSV parsing, residency metadata integration, retention/release handoff, and real commit executors for content and taxonomy beyond loud failure remain open.
  • [~] tenant-integrations-api-keys-webhooks — dedicated V1 section 20.6 journey added for tenant/operator integration management. The real-BFF spec now covers platform-admin scope gates, API key create/rotate/revoke with raw-token non-leakage, downstream x-oshun-api-key tenant-resource auth with tenant/scope/IP/revoked boundaries and lastUsedAt read-back, webhook create/signed sandbox simulator/inactive fail-closed simulator, outbound dispatch handoff, partner receipt retry/dead-letter/success outcomes, non-sandbox simulator rejection, connector upgrade, real connector health probes, malformed payload rejection, and audit-search read-back. Tenant-admin UI controls and autonomous outbound retry/replay workers remain open.
  • tenant-oneroster-roster-sync — dedicated journey added for the V1 OneRoster leg. The real-BFF spec now covers tenant-console-scoped dry-run, conflict reporting, apply commit, list read-back, periodic SIS pull reconciliation, duplicate commit/pull rejection, malformed payload rejection, and conflict fail-closed behavior, including tenant.oneroster.commit.applied, tenant.oneroster.pull.reconciled, and tenant.oneroster.pull.conflicts_detected audit-log read-back plus duplicate/conflict no-phantom-audit guards. /operator/lms now covers the dedicated operator diff table through real /v1/admin/lms/oneroster/preview, including clean diff rows, blocked conflict rows, and no-mutation read-back.
  • [~] tenant-sso-config-saml-oidc-claim-mapping/operator/sso now renders live ssoConnectionStore cards seeded by real admin SSO writes; active/pending colour coding, IdP metadata URL, and last-sync timestamp are covered without the retired static SSO fixture. The browser editor also persists every claim slot and transform picker value through the live admin SSO BFF, and the browser Test action renders reachable plus fail-closed probe verdicts from the real admin SSO BFF.
  • [~] tenant-audit-log-investigation/operator/audit rebuilt in 9ba3d0823d as a real table (when/actor/ action/target) sorted newest first; AUDIT_FIXTURE has three events spanning incident.acknowledge, sso.connection.activated, tenant.member.added.

Editorial / Themis (2)#

  • [~] editorial-review-approval/lilith-studio gates correctly and /operator/admin renders the review queue. Current specs cover the audited decision panel and release-stream API; the author-side submit/publish leg remains partial.
  • [~] themis-assessment-appeal — real /v1/metis/integrity/* appeal route + resolve/adjudicate HTTP coverage now drives filed appeal, operator resolve, teacher overturn, teacher uphold, audit read-back, validation/scope failure gates, and customer /v1/metis/assessment-submissions submit→Themis adjudication→appeal handoff, learner-safe audit rationale/evidence/classifier detail, and /operator/metis live appeal queue/read/outcome-decision UI, upheld-without-client- teacherOverride, /messages decision notification read/follow/mark-read coverage, and terminal gradebook correction read-back; no current themis-assessment-appeal gaps remain tracked

Platform integrations (1)#

  • lms-connectors-operator — deep E2E coverage verifies tenant/platform scope gates, descriptor validation, managed connector lifecycle, environment override/restore, secret-free read-back, fail-closed LTI launch, the live /operator/lms roster, and the OneRoster preview table.

Summary#

  • Total: 58
  • Walked: 4 (onboarding-ten-steps, pwa-update-flow, tenant-oneroster-roster-sync, and lms-connectors-operator)
  • Partial: 54 (after goal-3 + goal-4 — every surface renders, every /v1/* endpoint returns a typed shape with real fixtures, every form POSTs to a real BFF endpoint with 422 + 202 branches, and the four formerly-blocked PWA/Studio journeys reclassify to [~] after goal-4 re-walk)
  • Failed: 0
  • Blocked: 0 (the previously-blocked install-as-pwa, pwa-update-flow, offline-first-time-use, lilith-studio-tara-scene-publish were all re-walked in goal-4 and confirmed unblocked; the live downstream flows that depended on real systems are now noted inline rather than carrying a separate [B] marker)
  • Skipped: 0
  • Remaining: 0

External-dependency decision records#

Goals 5–7 opened seven runbooks for work that then required either an external service choice or a canonical contract decision. They remain under external-dependencies/ as dated decision records:

  • External runtime/provider work: crypto-merchant-settlement, real-sit-audio-cdn
  • Contract decisions now implemented in libs/contracts: nyx-logged-observation-contract, library-collection-contract, atelier-scene-contract, studio-scene-contract, metis-course-proposal-contract

The index distinguishes historical closure context from current gaps; it no longer presents the five shipped contracts as absent.

Goal-3 build-out summary#

Following the goal-2 stub-coverage pass, every previously-stub sub-route page was rebuilt as a real Lilith-design-system surface fed by the BFF. Highlights:

  • Server-side BFF helperapps/oshun/web/src/lib/server/bff-fetch.ts forwards the oshun-session / __session cookie from the RSC request, caches at 60s revalidate, and returns null on failure so callers fall back to fixture data without crashing the route.
  • Real list and form views — every sub-route now renders the cream-paper, terracotta-accent masthead pattern with live BFF data: /arete/habits (list + check-in chips), /arete/habits/new (proposal form), /arete/offerings (drafts + sent), /nyx/tonight (timeline with accent lead), /nyx/observation (POST log form), /nyx/events + /events (cross-domain agenda + ICS subscribe), /profile/{data,notifications,persona,telegram}, /operator/{incidents,audit,sso,tenants} (real tables with severity color coding), /metis/{lessons,session,ingest,courses/new}, /veritas/evidence (drift + retraction two-pane), /sophia (witness-anchored answer), /nisaba/{notebooks,notebook/new}, /atelier/{camera-obscura,new}, /library/collections/{,new}, and /lilith-studio/{tara,scene/new}.
  • New write endpoints in apps/oshun/bff/src/routes/domain-stubs.ts with 422 validation branches and 202 accepted state: POST /v1/arete/habits, POST /v1/nyx/observations, POST /v1/metis/courses, POST /v1/library/collections, POST /v1/atelier/scenes, POST /v1/lilith-studio/scenes.
  • Enriched fixturesINCIDENTS_FIXTURE, AUDIT_FIXTURE, SSO_FIXTURE, TENANTS_FIXTURE, LIBRARY_ITEMS_FIXTURE, LIBRARY_COLLECTIONS_FIXTURE, ATELIER_JOBS_FIXTURE, ATELIER_SCENES_FIXTURE, PERSONA_VOICE_FIXTURE all now contain realistic rows so the new pages render with meaningful content out of the box.
  • Alias cleanups/arete/weekly → 301 to /arete/review; /metis/upload → 308 to /metis/byom.
  • Prod build unblockednode:crypto was finally severed from the client bundle (@noble/hashes/sha2 in provenance-bundle-helpers.ts plus a webpack.NormalModuleReplacementPlugin backstop for any remaining workspace lib that gets pulled in via a barrel re-export). pnpm build emits .next/BUILD_ID for all 700+ routes.
  • Build noise — removed the Next-15-only typedRoutes + turbopack top-level keys from the Next-14 config; suppressed the onnxruntime-web "Critical dependency" warning narrowly via config.ignoreWarnings.
  • CC-BUG-E remediated — in-memory dev customer-auth store now defaults emailVerified: false so it mirrors production; tests can opt back in with OSHUN_DEV_AUTO_VERIFY_EMAIL=true or NODE_ENV=test.

Goal-4 + 5 build-out summary#

Followup work after goal-3 to close the remaining gaps the previous "what's left" audit named:

  • bffGet cookie names correctedapps/oshun/web/src/lib/server/bff-fetch.ts was forwarding oshun_session (underscore) which the Next.js proxy never sets. Replaced with oshun-session + the __session Vercel-style fallback, matching apps/oshun/web/src/proxy.ts.
  • Mobile responsive — appended a single @media (max-width: 720px) block to globals.css that targets React's serialized inline grid-template-columns strings so all 27 new sub-route pages collapse to single column on phones with reduced padding; zero source edits to the pages themselves.
  • Accessibility pass — every toggle group (cadence/conditions/source- kind/visibility/scene-kind) now wraps in role="radiogroup" + aria-label; every accepted-state success card carries role="status" and aria-live="polite"; toggle buttons have focus-visible terracotta outlines.
  • CSRF token + cookie pair — new GET /v1/csrf mints a 64-char hex token in oshun-csrf cookie (Path=/, SameSite=Lax, Secure-when-https); csrfGuard preHandler chained after originGuard on every state-changing POST rejects 403 csrf_token_invalid on missing/mismatched tokens. Client cache in apps/oshun/web/src/lib/csrf.ts does the handshake once with single-flight inflight handling. All 8 new client forms updated to send the token. The earlier origin-only check stays as the floor.
  • File-backed persistence — the in-memory store from goal-3 (which wiped on BFF restart) now writes to a single JSON snapshot at ${HOME}/.oshun-dev/domain-stubs-store.json (or OSHUN_DEV_STUBS_STORE_PATH) via atomic rename. NODE_ENV=test falls back to pure in-memory. Real Prisma persistence stays a follow-up because the canonical AreteHabit schema carries tenant + tombstone + audit semantics that would force a prisma migrate dev run against the shared dev database, risky for parallel worktrees.
  • node:crypto@noble/hashes in client-transpiled libs — migrated lora-manager.ts, evidence-sophia/source-{set,lifecycle}.ts, messaging-channels/telegram/security.ts (HMAC + timing-safe equal + base64url all rewritten), privacy/export.ts, platform-foundations/oauth.ts, persistence/migration-plan.ts. The other ~810 importers are server-only; the NormalModuleReplacementPlugin backstop in next.config.mjs still covers them. Also bulk-fixed ignoreDeprecations: "6.0" → "5.0" across 128 lib + V4 + tools tsconfigs.
  • Dev-session bypassPOST /api/dev/seed-session provisions a real signed-in session for Playwright tests; sets both oshun-session and oshun-access cookies. Disabled in production. Verified end-to-end: seeded session, then walked /, /tara, /arete/habits, /nyx/tonight, /profile/data — all return 200 with the correct h1, no /welcome bounce, no reauth loop.
  • Test coverage — 54 vitest specs for the goal-3 BFF surface (domain-stubs-route.test.ts + domain-stubs-store.test.ts): GET shape contracts, POST 422/202 branches, meaningful fixture content, CSRF guard cases, file-backed round-trip + rehydrate, corrupt-snapshot recovery.
  • Tracker housekeeping — the four formerly-blocked PWA/studio journeys reclassified [B][~] after goal-4 re-walk; blocked count now 0.

Goal-2 build-out summary#

Commits 03d45f0ad7 (BFF + pages) and e25187874a plus the build-fix sequence brought every previously-blocked journey to "partial" by adding:

  • ~40 BFF stub endpoints in apps/oshun/bff/src/routes/domain-stubs.ts — every /v1/* URL referenced by a journey spec now returns a typed JSON shape derived from the same fixtures the customer surfaces use
  • 27 sub-route stub pages under apps/oshun/web/src/app/ — Arete habits/offerings/weekly, Nyx tonight/observation/events, Nisaba notebooks/notebook-new, Metis lessons/session/upload/ingest/courses-new, Veritas evidence, Sophia, Atelier rooms + new, Library collections, Profile data/notifications/persona/telegram, Operator incidents/audit/sso/tenants, Lilith-Studio tara/scene-new — each renders with a proper h1 and related-route navigation
  • Trimmed barrels: @oshun/memory-iris (multi-actor), @yemaya/living-scenes-runtime (cue-privacy / personal-artifacts / envelope), @yemaya/remote-film-capture (56 cross-domain integration files) — server-only modules importing node:crypto, onnxruntime-web, or @aja/* @aphrodite/* @maya/* @isis/* @bellona/* @oya/* @uzume/* @calliope/* @hathor/* @iris/* shim packages no longer get pulled into client bundles via barrel re-exports
  • Webpack aliases: onnxruntime-web → dist/ort.min.js (browser CommonJS bundle) so SSR doesn't choke on the .mjs entry that declares import { createRequire } from "module" at top level

Goal-2 conclusion (historical)#

At the end of goal 2, the web app's customer surfaces rendered correctly in development after the fixes listed below. The remaining gaps at that checkpoint were feature or external-runtime work rather than broken routing.

Now works that didn't before

  • Anonymous funnel: //welcome → signup → /onboarding/ and /tara
  • All 9 domain hubs render with proper <h1> for screen readers (/, /tara, /arete, /veritas, /nyx, /nisaba, /metis, /atelier, /lilith)
  • /tara/sit/<id> player has h1 = sitting title (was missing)
  • /welcome mode tabs are real [role="tablist"] with arrow-key nav
  • /landing pricing CTAs deep-link to /welcome?mode=signup&entry=marketing-landing&tier=…
  • Tara → Nisaba, Veritas → Nisaba, Nyx → Tara cross-domain bridges, including Nyx event-detail → Tara start telemetry
  • /scene/<id> tombstone + embed surface (P0 'use server' bug fixed)
  • /scene/* titles single-suffix " | OSHUN" (was double)
  • /profile/memory no longer crashes the dev server (node:crypto bundling fixed)
  • /billing index page exists (was 404)
  • PWA Service Worker registers + activates (install handler precache no longer fails on /'s 307 redirect)
  • PWA manifest theme/bg color matches Lilith cream (#f1ebdd)
  • Hydration is clean — no more 100+ console errors per render from the cookie-icon className mismatch
  • BFF feature-flag rate limit no longer trips on normal browsing
  • All public OpenGraph / Twitter card / JSON-LD metadata correct
  • Sign-up API works, sets 3 cookies, navigates to /onboarding
  • /search?q=meditation returns 13 results
  • /library renders 10 items
  • /arete/streak Lilith tone copy verified (recovery framing, no shaming)
  • /operator/admin renders grounded-answer review queue

Needed deeper feature implementation, not surface fixes

  • The new sub-route pages render real data from the BFF, but the deepest write flows still depend on real downstream systems: crypto invoice settlement events, SCIM provisioning sync, agent orchestrator job execution, real audio for sit completion, real ONNX models for fine-detail matting, real Telegram bot for the link round-trip. The BFF surfaces them as 202-accepted stubs.
  • The four formerly blocked journeys — install-as-pwa, pwa-update-flow, offline-first-time-use, and lilith-studio-tara-scene-publish — were re-walked after CC-BUG-C and the studio editor route were fixed. They are now [x] or [~]; none remains blocked on the original failure.
  • Tenant admin (SCIM, SSO, audit) — lives in apps/oshun/tenant-admin per the product architecture, outside the main customer shell.

Walker artifacts (not bugs)

  • "Cookies after accept (0)" — consent stores in localStorage, not cookies; persistence verified working
  • "Banner reappears after reload" — old walker selector matched any "Accept" button; with proper selector the banner stays dismissed
  • Mid-test "reauth=1" ejection — Playwright test-race when BFF restarts during a long walk wipes in-memory sessions; verified with API signup that real sessions survive

Cross-cutting findings (apply to many journeys)#

These bugs were surfaced by journey 1 but affect most signed-in journeys — re-tested journeys do not need to re-document them, just cross-link:

  • CC-BUG-A (P0)fixed in a2908f1c89 + 3f130c64f1. Prod pnpm build now succeeds end-to-end and emits .next/BUILD_ID for all 700+ routes. Root causes resolved: (1) libs/contracts/src/common/provenance-bundle-helpers.ts swapped createHash('sha256') for @noble/hashes/sha2 so the contracts barrel no longer drags node:crypto into the client bundle; (2) next.config.mjs installs a webpack.NormalModuleReplacementPlugin that remaps any remaining node:* URL-scheme imports to a Proxy-backed CommonJS stub (apps/oshun/web/src/lib/server/node-crypto-stub.cjs) — this is the backstop for every other workspace lib that gets pulled into the client bundle via a barrel re-export; (3) tsconfig.base.json + apps/oshun/web/tsconfig.json ignoreDeprecations: "5.0" (the "6.0" value would only be accepted by TS 6).
  • CC-BUG-B (P0)fixed in adae8117dd. Hydration mismatch came from the workspace-hoisted lucide-react v0.344 (trailing-space className) being used by webpack SSR while client used the local v0.469. Added a webpack resolve.alias so both passes resolve to the app-local copy.
  • CC-BUG-C (P0)fixed in adae8117dd. SW now installs + activates. Root cause was two bugs: sw.js install handler called cache.addAll(['/' ...]) and / returned 307 → addAll rejected → SW went redundant; AND PwaBootstrap.useEffect only registered when document.readyState === 'complete' but post-hydration it's interactive. Removed / from precache and broadened the readyState check to !== 'loading'.
  • CC-BUG-D (P1)by design / transient. Re-investigated: BFF accepts its dev tokens correctly (/v1/profile returns 200 with valid Bearer token). The 401s in walks were initial-mount races (page fires the fetch before AuthContext populates the token; React Query retries successfully on the second attempt). Not a real bug; pages do see empty-shell flash briefly before populated.
  • CC-BUG-E (P1)addressed in this turn. The in-memory dev customer-auth store no longer hard-codes emailVerified: true at signup. The default flips to false so the dev store mirrors production; integration tests that need the legacy auto-verify behavior can set OSHUN_DEV_AUTO_VERIFY_EMAIL=true (or rely on the automatic exemption when NODE_ENV=test). See apps/oshun/bff/src/auth/customer-auth-store.ts signUp(…).
  • CC-BUG-F (P2)fixed in adae8117dd. BFF /v1/feature-flags/evaluate budget lifted from 60/min to 600/min so normal signed-in browsing doesn't trip the abuse-protection limiter.
  • CC-BUG-G (P0)fixed in adae8117dd. libs/oshun/memory-iris barrel-re-exported multi-actor/namespace which imports node:crypto; any client bundle that touched @oshun/memory-iris (including /profile/memory) blew up. Removed multi-actor from the barrel.
  • CC-BUG-H (P1)fixed in adae8117dd. Domain hub pages (/tara, /arete, /nyx, /nisaba, /metis, /atelier, /lilith) and the Tara sit player rendered their primary display text as styled <div>s instead of <h1> — a11y regression. LDisplay and LMasthead now default to rendering as <h1> (with as / titleAs overrides for nested displays); /nisaba and /metis inline display divs converted to <h1> too.
  • CC-BUG-I (P2)fixed in adae8117dd. /scene/* titles double-suffixed " | OSHUN | OSHUN" because page metadata returned strings already ending in " | OSHUN" while the root layout's title template wrapped them again.
  • CC-BUG-J (P1)fixed in 860a554b4e. Cross-domain bridges (Tara→Nisaba, Veritas→Nisaba, Nyx→Tara) were missing from the hub pages. Added optional nisabaPassageCue / nisabaDeepReadCue / taraGroundingCue slots to the data contracts + render bridges in rooms.tsx.
  • CC-BUG-K (P1)fixed in 860a554b4e. /landing pricing CTAs now deep-link to /welcome?mode=signup&entry=marketing-landing&tier=… instead of bare /welcome (per-tier funnel attribution).
  • CC-BUG-L (P2)fixed in 860a554b4e. Manifest theme + bg color was dark navy (#020617); fixed to cream (#f1ebdd) so standalone PWA splash matches the Lilith design system.
  • CC-BUG-M (P1)fixed in ead9a957f7. WelcomeAuthPanel mode buttons now have proper [role="tablist"] / [role="tab"] / aria-selected / arrow-key navigation.

Method#

For each journey I:

  1. Read WALKTHROUGH/journeys/<journey>.md for steps + per-view file refs
  2. Drive Playwright (chromium, headless by default) through each step
  3. Capture a screenshot per major step into the screenshots dir
  4. Mark each numbered step pass/fail with observed behavior and the actual URL, selector, telemetry event seen vs. expected
  5. Note any divergence from the spec — wrong copy, missing CTA, broken link, crashed page, unimplemented feature, accessibility gap
  6. Update the status checkbox in this README

A journey is only [x] walked when every numbered step verified. Anything short of that gets [~], [F], or [B] with the specific failure.