V1 Web PWA · Surface walkthrough

Library

A per-surface walkthrough of the V1 Web PWA customer surface: layout, states, interactions, data, and cross-references.

walked
9sections10 minread

On this page

Context. surface customer · domain discovery · route /library · auth signed-in · source apps/oshun/web/src/app/library/page.tsx

Last walked. 2026-06-27 automated coverage reconciliation by Codex — LibraryDashboard component states, collection-management Playwright, saved-items real-BFF sync, cross-device hydrate, share links, offline collection writes, shell-state previews, viewport fit, BFF aggregation contracts, and docs center generation cross-checked.

Purpose#

Everything the user has saved — sits, sources, passages, sky events, courses, assistant excerpts, notebooks, generated artifacts, assessment queues, tutoring threads — kept close in one memory surface, with filter-based and named collections that let one active thread stay together across the six domains.

Entry points#

  • Shell nav: Library tabWEB_SHELL_ROUTE_PATHS.library; primary shell route
  • Focal-stage shortcut — Activity and other shell surfaces' "Open Library" card
  • Save action from any result — Search results, Explore curated cards, and domain rooms toggle the same useOshunWebLibraryStore store; library shows the same items
  • Library collection deep link/library/collection/<collectionId> lands in the same dashboard with the collection pre-selected (see library-collection-collectionId.md)
  • Direct URL / bookmark — yes; auth required (relies on shell middleware)
  • Shell state preview?shellState=<mode> swaps content via resolveShellRouteSurfaceContent({ surface: 'library' })

Layout regions#

page.tsx mounts ShellLayout with active="library" and breadcrumbs Home → Library, then renders <LibraryDashboard /> (or the shell-state preview).

Inside LibraryDashboard:

  • Pre-hydration placeholder — when hasMounted === false, only a ShellRouteAnchor ("Keep what matters, then return to it deliberately") is rendered with aria-busy="true"
  • Focal stageShellRouteAnchor (var(--l-warn) accent) + ShellLandingFocalStage with the current "library frame title", summary metric pills, active return card, collection shortcuts, current posture, and Metis return-surfaces summary
  • Saved-items cap noticeLibrarySaveCapNotice renders the inline Pro paywall when the signed-in free member reaches the 25 real-save cap
  • Research-to-practice journey railResearchPracticeJourneyRail driven by resolveResearchPracticeStageIdFromLibraryFilters
  • Workspace toolbarWorkspaceToolbar with WorkspaceSegmentRail filters:
    • Domain segment railFilterRail<LibraryDomainFilter> (All / Tara / Veritas / Nyx / Arete / Nisaba / Metis)
    • Kind segment railLIBRARY_KIND_FILTERS (all / meditation / story / claim / source / passage / notebook / collection / program / ritual / sky-event / course / generated-artifact / assessment / tutoring / assistant-excerpt)
  • Saved queue panelShellSavedQueuePanel supports kind filters, sort modes, snooze, complete, and domain resume links
  • Collections lane — list of CollectionCards for both default (LIBRARY_COLLECTIONS) and user-created collections, with editable label / description / followUp when a collection is selected
  • Create from filters — collection create button in the collections lane; disabled when the current filtered save set is empty
  • Filtered saved items listLibraryItem cards with LibraryItemSignals (confidence, grounded-evidence, provenance badges), optional retraction notice, resume link, and save toggle
  • Stats — saved count, distinct domains, study / re-entry kind breakdown
  • Support lane (WorkspaceSupportLane) — docked on wide desktop; collapses on laptop; stacks on tablet/mobile. Contains the top 3 resume ContinuityCards, selected-collection manager, share controls, and the route relationship note.

States#

  • Loading (pre-hydration)hasMounted === false shows only the anchor stub with aria-busy="true" and "Loading saved continuity, collections, and the next best return point." Covered by LibraryDashboard.test.tsx via the server render pass.
  • Empty saved-items store — empty libraryItems shows data-library-empty, disables "Create from current filters", renders no resume cards, and keeps default collections at 0 items. Covered by LibraryDashboard.test.tsx.
  • Populated (filters: all/all) — saved items, metric pills, focal stage, saved queue, collections, continue lane, and trust signals render from the web store. Covered by viewport-fit.spec.ts, library-save-collection-share.spec.ts, and LibraryDashboard.test.tsx.
  • Populated (domain filter active) — frame title and status update from LIBRARY_DOMAIN_LABELS[domain]; saved rows narrow to that domain and Clear filters appears. Covered by library-collection-management.spec.ts and LibraryDashboard.test.tsx.
  • Populated (kind filter active) — frame title and rows narrow from LIBRARY_KIND_LABELS[kind]; typed saves include claim, source, passage, notebook, collection, program, ritual, sky-event, course, generated-artifact, assessment, tutoring, and assistant excerpt. Covered by LibraryDashboard.test.tsx.
  • Collection selected/library/collection/<id> passes initialCollectionId, swaps the focal frame to collection copy, opens the support-lane manager, and marks the selected collection active. Covered by library-collection-management.spec.ts.
  • Collection editor — selected collections populate label, description, and follow-up fields; save writes edited details back to the collection list. Covered by library-collection-management.spec.ts and LibraryDashboard.test.tsx.
  • Stored collections missing / corruptedreadStoredLibraryCollections() returns null; defaults still render and direct default collection links work. Covered by library-collection-management.spec.ts and LibraryDashboard.test.tsx.
  • Legacy stored collections migrate — payload version: 0 normalizes to version 1, stale item ids are removed, duplicate ids collapse, and the current payload is persisted. Covered by library-collection-management.spec.ts and LibraryDashboard.test.tsx.
  • Collection storage write — collection edits serialize to localStorage['oshun.library.collections.v1'] once collection hydration completes. Covered by library-collection-management.spec.ts reload assertions and offline-write-deepening.spec.ts.
  • Item save togglehandleLibraryItemToggle removes the row, updates the saved-items store, and fires trackLibraryItemUnsaved from the library saved-items surface. Covered by library-save-collection-share.spec.ts, library-save-analytics.spec.ts, and LibraryDashboard.test.tsx.
  • Signed-in saved-items hydrate — signed-in users reconcile from the per-user BFF saved-items store (GET /v1/library/saved-items); server saves from another device render on a fresh browser. Covered by library-cross-device-render.spec.ts and library-entitlement-and-save.spec.ts.
  • Saved-items cap — a free member at 25 real saves sees the inline Pro paywall at #library-saved-cap; showcase/demo items do not count. Covered by library-entitlement-and-save.spec.ts and LibraryDashboard.test.tsx.
  • Reduced motion — route-owned animated trust badges use disableAnimation; broader shell reduced-motion behavior is covered by PWA/shell animation checks, while Library has no separate motion branch.
  • Shell state preview?shellState=loading|empty|offline|... replaces the live workspace with ShellSurfaceStatePreview. Covered by shell-surface-states.spec.ts.

Interactions#

Focal stage#

  • Frame title / summary — non-interactive libraryFrameTitle and libraryFrameSummary; active filters and selected collections change the copy. Covered by LibraryDashboard.test.tsx.
  • Metric pillsShellLandingMetricPill for saved count, domains represented, study/source count, and practice/sky/assistant returns. Covered by viewport-fit.spec.ts and LibraryDashboard.test.tsx.
  • Active returndata-library-active-return-action opens buildLibraryResumeHref(focalReturn.domain, focalReturn.resumePath). Covered through resume-card navigation in library-save-collection-share.spec.ts and component route assertions.
  • Collection shortcuts — focal rail opens the first collections through navigateToCollection(collection.id). Covered by LibraryDashboard.test.tsx.
  • Metis return surfaces — saved course, study queue, and recent tutoring counts stay distinct; best/queue/recent buttons route to their stored resume targets. Covered by LibraryDashboard.test.tsx.
  • Primary shortcuts — Explore more, Review activity, and Ask assistant route/dispatch from the hero actions. Assistant launch is covered by LibraryDashboard.test.tsx; sibling route visibility is covered by route viewport/signoff specs.

Workspace toolbar#

  • Domain segment rail (FilterRail<LibraryDomainFilter>, aria-label "Library domain focus")
    • Function: sets domainFilter; updates frame title/summary
    • Coverage: library-collection-management.spec.ts, viewport-fit.spec.ts, and LibraryDashboard.test.tsx.
  • Kind segment rail (FilterRail<LibraryKindFilter>, aria-label "Library save type")
    • Function: sets kindFilter
    • Coverage: LibraryDashboard.test.tsx.
  • Clear filters (data-library-filter-reset)
    • Function: resets domainFilter and kindFilter to all
    • Coverage: library-collection-management.spec.ts and component tests.
  • Create collection from filters (handleCreateCollectionFromFilters)
    • Function: clones the current filteredItems into a new collection with a derived label; navigates to /library/collection/<slug>
    • Disabled when: filteredItems.length === 0
    • Coverage: library-collection-management.spec.ts and LibraryDashboard.test.tsx.

Collections lane#

  • Collection card click (CollectionCard) → navigates to /library/collection/<id> via router.push(buildLibraryCollectionHref)
  • Edit label / description / followUp — inputs update collectionEditor; syncCollection writes back to collections
  • Add / remove item from collectionsetCollectionItemIds mutates the collection's item set
  • Archive collectionhandleArchiveCollection() removes the selected collection when more than one collection exists, deletes its item-id map, and navigates to the next remaining collection
  • Share controls — visibility, permission, expiry, channel, invite, referral, private, named-user, and public-profile controls call the real collection sharing BFF routes. Covered by collection-share.spec.ts, library-save-collection-share.spec.ts, and library-shared-web-view.spec.ts.

Coverage: collection navigation, create/edit/archive, storage hydration, legacy migration, corrupted payload fallback, and item membership are covered by library-collection-management.spec.ts and LibraryDashboard.test.tsx.

Saved queue#

  • Queue kind filter / sort modeShellSavedQueuePanel filters bookmarks, reading list, later saves, tasks, and queue items, then sorts smart/due/resume/domain. Covered by library-save-collection-share.spec.ts and ShellSavedQueuePanel.test.tsx.
  • Snooze / complete — buttons mutate queue status and persist to localStorage['oshun.shell.saved-queue.v1']. Covered by library-save-collection-share.spec.ts and ShellSavedQueuePanel.test.tsx.
  • Resume — queue resume pushes a hydrated domain route with queueItem and queueKind params. Covered by library-save-collection-share.spec.ts.

Priority resume rail#

  • Top 3 resume cards (ContinuityCard) — open buildLibraryResumeHref (domain root + ?path=...). Covered by library-save-collection-share.spec.ts and LibraryDashboard.test.tsx.

Research-to-practice rail#

  • ResearchPracticeJourneyRail — stage derived from current filters (resolveResearchPracticeStageIdFromLibraryFilters); route actions open their hrefs and assistant actions dispatch the assistant entry event. Covered by ResearchPracticeJourneyRail.test.tsx and LibraryDashboard.test.tsx.

Saved item row#

  • Open — resume button pushes buildLibraryResumeHref(item.domain, item.resumePath).
  • Ask assistant — per-row assistant button dispatches dispatchOshunAssistantOpen({ source: "library:<item.id>" }).
  • Save togglehandleLibraryItemToggle(item) flips persistence; trackLibraryItemUnsaved analytics on removal
  • Signal badges (ConfidenceBadge, LibraryGroundingBadge, ProvenanceBadge) — non-interactive trust indicators
  • Retraction notice — affected saved claims show corrected-source, remove, and appeal/correct actions. Covered by LibraryDashboard.test.tsx.

Coverage: row removal, telemetry, trust badges, retraction actions, and notebook card rendering are covered by library-save-analytics.spec.ts, library-save-collection-share.spec.ts, and LibraryDashboard.test.tsx.

Metis surfaces summary#

  • Best resume / queued item / recent session cards — open their stored resume targets and keep saved courses, assessment queue, and tutoring sessions as separate counts. Covered by LibraryDashboard.test.tsx and BFF Metis-scoped aggregation in library-route.test.ts.

Assistant entry#

  • Assistant launchers within cards — call dispatchOshunAssistantOpen(...) to surface the in-app assistant. Covered by LibraryDashboard.test.tsx and assistant save telemetry in library-save-analytics.spec.ts.

Data & contracts#

  • Reads:
    • useOshunWebLibraryStore() (client store; see @/lib/library/webLibraryStore) — returns { items: WebLibrarySavedItem[] }
    • GET /v1/library/saved-items via createSavedItemsClient().getSnapshot() for signed-in users; rows map through contractItemToWebItem
    • DEFAULT_WEB_LIBRARY_ITEMS — guest/offline showcase bootstrap data shape
    • localStorage['oshun.library.collections.v1'] — persisted user collections payload (StoredLibraryCollectionsPayload)
    • localStorage['oshun.shell.saved-queue.v1'] — saved queue status, snooze, and completion state
  • Writes:
    • toggleOshunWebLibraryItem(item) — local optimistic save/unsave; authenticated real saves call POST/DELETE /v1/library/saved-items/:domain/:id in the background
    • PUT /v1/library/saved-items/sync — pending anonymous saves migrate after sign-in
    • localStorage.setItem(LIBRARY_COLLECTION_STORAGE_KEY, …) — serialized collection state
    • localStorage.setItem('oshun.shell.saved-queue.v1', …) — serialized queue state
    • POST/PUT/DELETE /v1/library/collections/:collectionId/share... — tokenized collection links, visibility, and revocation
  • Realtime: None.
  • Caching: client store + localStorage; signed-in server sync is fail-soft and retried on later hydration/profile changes
  • Auth/role check: shell middleware
  • Telemetry: trackLibraryItemUnsaved; cross-surface save telemetry also records item_saved / item_unsaved from Explore, Search, Library, and assistant-message surfaces

Automated coverage#

  • apps/oshun/web/src/components/library/__tests__/LibraryDashboard.test.tsx — empty/pre-hydration states, filters, collection editor, corrupted/legacy collection payloads, trust/retraction signals, Metis surfaces, assistant launcher, save-cap notice, and unsave telemetry.
  • apps/oshun/web/e2e/library-collection-management.spec.ts — direct collection links, unknown-id fallback with query filters, stored/legacy collection hydration, create/edit/archive, and desktop/laptop support lane.
  • apps/oshun/web/e2e/library-save-collection-share.spec.ts — continue cards, saved queue, sharing-control presence, row unsave, and anonymous-to-signed-in saved-item migration.
  • apps/oshun/web/e2e/collection-share.spec.ts and apps/oshun/web/e2e/library-shared-web-view.spec.ts — real BFF collection share creation, visibility, revoke/private paths, and recipient rendering.
  • apps/oshun/web/e2e/library-cross-device-render.spec.ts and apps/oshun/web/e2e/library-entitlement-and-save.spec.ts — real saved-items BFF sync, cross-device read, tombstone propagation, cap paywall, and aggregate /v1/library contracts.
  • apps/oshun/web/e2e/offline-write-deepening.spec.ts — collection membership edits persist locally while offline.
  • apps/oshun/web/e2e/viewport-fit.spec.ts and apps/oshun/web/e2e/shell-surface-states.spec.ts — Library layout fit and ?shellState= preview modes.
  • apps/oshun/bff/src/__tests__/library-route.test.ts — BFF aggregation, domain filtering, Metis-scoped items, partial failure headers, and trace metadata.

Cross-references#

Open questions / known gaps#

  • Resolved: signed-in saved items hydrate from /v1/library/saved-items, not only local client state.
  • Resolved: collection payload version: 0 migrates to LIBRARY_COLLECTION_STORAGE_VERSION (1), sanitizing stale/duplicate item ids.
  • Resolved: the UI affordance is Archive collection; it removes whichever collection is currently selected when more than one collection exists.
  • Residual manual pass: live screen-reader/touch review for the full Library page remains useful, but automated component, Playwright, BFF, offline, viewport, and shell-state coverage is in place.